JS: prototyping coroutines
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
var exception: Throwable? = null
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Any>) {}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var result = "fail"
|
||||
operator fun handleResult(u: Unit, c: Continuation<Nothing>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var result = false
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(a: String = "abc", i: Int = 2, x: Continuation<String>) {
|
||||
x.resume(a + "#" + (i + 1))
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
var result = 0
|
||||
|
||||
class Controller {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun <T> suspendHere(v: T, x: Continuation<T>) {
|
||||
x.resume(v)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
// FULL_JDK
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
var exception: Throwable? = null
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var ok = false
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var isCompleted = false
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
fun withValue(v: String, x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var i = 0
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("OK")
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var result = "fail"
|
||||
suspend fun <V> suspendHere(v: V, x: Continuation<V>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(v: String, x: Continuation<String>) {
|
||||
x.resume(v)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var result = ""
|
||||
var ok = false
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var wasHandleResultCalled = false
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var wasHandleResultCalled = false
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var ok = false
|
||||
var v = "fail"
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("OK")
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var lastSuspension: Continuation<String>? = null
|
||||
var result = "fail"
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var res = 0
|
||||
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var cResult = 0
|
||||
suspend fun suspendHere(v: Int, x: Continuation<Int>) {
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Controller {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var res = 0
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("OK")
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resumeWithException(RuntimeException("OK"))
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var res = 0
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
var globalResult = ""
|
||||
class Controller {
|
||||
suspend fun suspendWithValue(v: String, x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
suspendThere(x)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
@AllowSuspendExtensions
|
||||
class Controller {
|
||||
suspend fun String.suspendHere(x: Continuation<String>) {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(v: Int, x: Continuation<Int>) {
|
||||
x.resume(v * 2)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
var i = 0
|
||||
suspend fun suspendHere(x: Continuation<Int>) {
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("K")
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(v: String, x: Continuation<String>) {
|
||||
x.resume(v)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
// WITH_RUNTIME
|
||||
var globalResult = ""
|
||||
var wasCalled = false
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("OK")
|
||||
|
||||
-3
@@ -1,6 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<String>) {
|
||||
x.resume("OK")
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ package com.google.dart.compiler.backend.js.ast.metadata
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
internal class MetadataProperty<in T : HasMetadata, R>(val default: R) {
|
||||
class MetadataProperty<in T : HasMetadata, R>(val default: R) {
|
||||
operator fun getValue(thisRef: T, desc: KProperty<*>): R {
|
||||
if (!thisRef.hasData(desc.name)) return default
|
||||
return thisRef.getData<R>(desc.name)
|
||||
|
||||
+9
@@ -20,6 +20,7 @@ package com.google.dart.compiler.backend.js.ast.metadata
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineStrategy
|
||||
|
||||
@@ -57,6 +58,14 @@ var HasMetadata.synthetic: Boolean by MetadataProperty(default = false)
|
||||
|
||||
var HasMetadata.sideEffects: SideEffectKind by MetadataProperty(default = SideEffectKind.AFFECTS_STATE)
|
||||
|
||||
var JsFunction.coroutineType: ClassDescriptor? by MetadataProperty(default = null)
|
||||
|
||||
var JsFunction.controllerType: ClassDescriptor? by MetadataProperty(default = null)
|
||||
|
||||
var JsInvocation.isSuspend: Boolean by MetadataProperty(default = false)
|
||||
|
||||
var JsInvocation.isHandleResult: Boolean by MetadataProperty(default = false)
|
||||
|
||||
enum class TypeCheck {
|
||||
TYPEOF,
|
||||
INSTANCEOF,
|
||||
|
||||
@@ -68,6 +68,12 @@ class NameSuggestion {
|
||||
return suggest(descriptor.containingDeclaration!!)
|
||||
}
|
||||
|
||||
if (descriptor is FunctionDescriptor && descriptor.isSuspend) {
|
||||
descriptor.initialSignatureDescriptor?.let {
|
||||
return suggest(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic declarations always require stable names as defined in Kotlin source code
|
||||
if (descriptor.isDynamic()) {
|
||||
return SuggestedName(listOf(descriptor.name.asString()), true, descriptor, descriptor.containingDeclaration!!)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.coroutine
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsBlock
|
||||
import com.google.dart.compiler.backend.js.ast.JsStatement
|
||||
|
||||
class CoroutineBlock {
|
||||
val statements = mutableListOf<JsStatement>()
|
||||
val jsBlock = JsBlock(statements)
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.coroutine
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperty
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.isSuspend
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
class CoroutineBodyTransformer(val program: JsProgram, val scope: JsScope, val throwFunctionName: JsName?) : RecursiveJsVisitor() {
|
||||
val entryBlock = CoroutineBlock()
|
||||
val globalCatchBlock = CoroutineBlock()
|
||||
private var currentBlock = entryBlock
|
||||
private val currentStatements: MutableList<JsStatement>
|
||||
get() = currentBlock.statements
|
||||
val resultFieldName by lazy { scope.declareFreshName("\$result") }
|
||||
val stateFieldName by lazy { scope.declareFreshName("\$state") }
|
||||
val controllerFieldName by lazy { scope.declareFreshName("\$controller") }
|
||||
val exceptionStateName by lazy { scope.declareFreshName("\$exceptionState") }
|
||||
private val breakTargets = mutableMapOf<JsName, JsStatement>()
|
||||
private val continueTargets = mutableMapOf<JsName, JsStatement>()
|
||||
private var defaultBreakTarget: JsStatement = JsEmpty
|
||||
private var defaultContinueTarget: JsStatement = JsEmpty
|
||||
private val referencedBlocks = mutableSetOf<CoroutineBlock>()
|
||||
private val breakBlocks = mutableMapOf<JsStatement, CoroutineBlock>()
|
||||
private val continueBlocks = mutableMapOf<JsStatement, CoroutineBlock>()
|
||||
private lateinit var nodesToSplit: Set<JsNode>
|
||||
private var currentCatchBlock = globalCatchBlock
|
||||
|
||||
fun preProcess(node: JsNode) {
|
||||
val nodes = mutableSetOf<JsNode>()
|
||||
|
||||
node.accept(object : RecursiveJsVisitor() {
|
||||
var childrenInSet = false
|
||||
|
||||
override fun visitInvocation(invocation: JsInvocation) {
|
||||
super.visitInvocation(invocation)
|
||||
if (invocation.isSuspend) {
|
||||
nodes += invocation
|
||||
childrenInSet = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitElement(node: JsNode) {
|
||||
val oldChildrenInSet = childrenInSet
|
||||
childrenInSet = false
|
||||
node.acceptChildren(this)
|
||||
if (childrenInSet) {
|
||||
nodes += node
|
||||
}
|
||||
else {
|
||||
childrenInSet = oldChildrenInSet
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
nodesToSplit = nodes
|
||||
}
|
||||
|
||||
fun postProcess(): List<CoroutineBlock> {
|
||||
if (entryBlock == currentBlock) return listOf(entryBlock)
|
||||
|
||||
currentBlock.statements += JsReturn()
|
||||
val orderedBlocks = DFS.topologicalOrder(listOf(entryBlock)) { it.collectTargetBlocks() }
|
||||
val blockIndexes = orderedBlocks.withIndex().associate { (index, block) -> Pair(block, index) }
|
||||
|
||||
val blockReplacementVisitor = object : JsVisitorWithContextImpl() {
|
||||
override fun endVisit(x: JsDebugger, ctx: JsContext<in JsStatement>) {
|
||||
val target = x.targetBlock
|
||||
if (target != null) {
|
||||
val lhs = JsNameRef(stateFieldName, JsLiteral.THIS)
|
||||
val rhs = program.getNumberLiteral(blockIndexes[target]!!)
|
||||
ctx.replaceMe(JsAstUtils.assignment(lhs, rhs).makeStmt())
|
||||
}
|
||||
|
||||
val exceptionTarget = x.targetExceptionBlock
|
||||
if (exceptionTarget != null) {
|
||||
val lhs = JsNameRef(exceptionStateName, JsLiteral.THIS)
|
||||
val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!)
|
||||
ctx.replaceMe(JsAstUtils.assignment(lhs, rhs).makeStmt())
|
||||
}
|
||||
}
|
||||
}
|
||||
for (block in orderedBlocks) {
|
||||
blockReplacementVisitor.accept(block.jsBlock)
|
||||
}
|
||||
|
||||
return orderedBlocks
|
||||
}
|
||||
|
||||
override fun visitBlock(x: JsBlock) = splitIfNecessary(x) {
|
||||
for (statement in x.statements) {
|
||||
statement.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIf(x: JsIf) = splitIfNecessary(x) {
|
||||
x.ifExpression = handleExpression(x.ifExpression)
|
||||
|
||||
val ifBlock = currentBlock
|
||||
|
||||
val thenEntryBlock = CoroutineBlock()
|
||||
currentBlock = thenEntryBlock
|
||||
x.thenStatement?.accept(this)
|
||||
val thenExitBlock = currentBlock
|
||||
|
||||
val elseEntryBlock = CoroutineBlock()
|
||||
currentBlock = elseEntryBlock
|
||||
x.elseStatement?.accept(this)
|
||||
val elseExitBlock = currentBlock
|
||||
|
||||
x.thenStatement = JsBlock(thenEntryBlock.statements)
|
||||
x.elseStatement = JsBlock(elseEntryBlock.statements)
|
||||
ifBlock.statements += x
|
||||
|
||||
val jointBlock = CoroutineBlock()
|
||||
thenExitBlock.statements += stateAndJump(jointBlock)
|
||||
elseExitBlock.statements += stateAndJump(jointBlock)
|
||||
currentBlock = jointBlock
|
||||
}
|
||||
|
||||
override fun visitLabel(x: JsLabel) {
|
||||
val inner = x.statement
|
||||
when (inner) {
|
||||
is JsDoWhile -> handleDoWhile(inner, x.name)
|
||||
|
||||
is JsWhile -> handleWhile(inner, x.name)
|
||||
|
||||
is JsFor -> handleFor(inner, x.name)
|
||||
|
||||
else -> splitIfNecessary(x) {
|
||||
val successor = CoroutineBlock()
|
||||
accept(inner)
|
||||
if (successor in referencedBlocks) {
|
||||
currentBlock.statements += stateAndJump(successor)
|
||||
currentBlock = successor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitWhile(x: JsWhile) = handleWhile(x, null)
|
||||
|
||||
private fun handleWhile(x: JsWhile, label: JsName?) = splitIfNecessary(x) {
|
||||
val predecessor = currentBlock
|
||||
val successor = CoroutineBlock()
|
||||
|
||||
val bodyEntryBlock = CoroutineBlock()
|
||||
currentBlock = bodyEntryBlock
|
||||
withBreakAndContinue(label, x, successor, bodyEntryBlock) {
|
||||
x.body.accept(this)
|
||||
}
|
||||
|
||||
if (x.condition != JsLiteral.TRUE) {
|
||||
val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source }
|
||||
bodyEntryBlock.statements.add(0, jsIf)
|
||||
}
|
||||
currentBlock.statements += stateAndJump(bodyEntryBlock)
|
||||
predecessor.statements += stateAndJump(bodyEntryBlock)
|
||||
currentBlock = successor
|
||||
}
|
||||
|
||||
override fun visitDoWhile(x: JsDoWhile) = handleDoWhile(x, null)
|
||||
|
||||
private fun handleDoWhile(x: JsDoWhile, label: JsName?) = splitIfNecessary(x) {
|
||||
val predecessor = currentBlock
|
||||
val successor = CoroutineBlock()
|
||||
|
||||
val bodyEntryBlock = CoroutineBlock()
|
||||
currentBlock = bodyEntryBlock
|
||||
withBreakAndContinue(label, x, successor, bodyEntryBlock) {
|
||||
x.body.accept(this)
|
||||
}
|
||||
|
||||
if (x.condition != JsLiteral.TRUE) {
|
||||
val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source }
|
||||
currentBlock.statements.add(jsIf)
|
||||
}
|
||||
currentBlock.statements += stateAndJump(bodyEntryBlock)
|
||||
predecessor.statements += stateAndJump(bodyEntryBlock)
|
||||
currentBlock = successor
|
||||
}
|
||||
|
||||
override fun visitFor(x: JsFor) = handleFor(x, null)
|
||||
|
||||
private fun handleFor(x: JsFor, label: JsName?) = splitIfNecessary(x) {
|
||||
x.initExpression?.let {
|
||||
JsExpressionStatement(it).accept(this)
|
||||
}
|
||||
x.initVars?.let { initVars ->
|
||||
if (initVars.vars.isNotEmpty()) {
|
||||
initVars.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
val predecessor = currentBlock
|
||||
val increment = CoroutineBlock()
|
||||
val successor = CoroutineBlock()
|
||||
|
||||
val bodyEntryBlock = CoroutineBlock()
|
||||
currentBlock = bodyEntryBlock
|
||||
withBreakAndContinue(label, x, successor, predecessor) {
|
||||
x.body.accept(this)
|
||||
}
|
||||
val bodyExitBlock = currentBlock
|
||||
|
||||
if (x.condition != null && x.condition != JsLiteral.TRUE) {
|
||||
val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor))).apply { source = x.source }
|
||||
bodyEntryBlock.statements.add(0, jsIf)
|
||||
}
|
||||
|
||||
bodyExitBlock.statements += stateAndJump(increment)
|
||||
currentBlock = increment
|
||||
|
||||
x.incrementExpression?.let { JsExpressionStatement(it).accept(this) }
|
||||
currentStatements += stateAndJump(bodyEntryBlock)
|
||||
|
||||
predecessor.statements += stateAndJump(bodyEntryBlock)
|
||||
currentBlock = successor
|
||||
}
|
||||
|
||||
override fun visitBreak(x: JsBreak) {
|
||||
val targetLabel = x.label?.name
|
||||
val targetStatement = if (targetLabel == null) {
|
||||
defaultBreakTarget
|
||||
}
|
||||
else {
|
||||
breakTargets[targetLabel]!!
|
||||
}
|
||||
|
||||
if (targetStatement in nodesToSplit) {
|
||||
val targetBlock = breakBlocks[targetStatement]!!
|
||||
referencedBlocks += targetBlock
|
||||
currentStatements += stateAndJump(targetBlock)
|
||||
}
|
||||
else {
|
||||
currentStatements += x
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitContinue(x: JsContinue) {
|
||||
val targetLabel = x.label?.name
|
||||
val targetStatement = if (targetLabel == null) {
|
||||
defaultContinueTarget
|
||||
}
|
||||
else {
|
||||
continueTargets[targetLabel]!!
|
||||
}
|
||||
|
||||
if (targetStatement in nodesToSplit) {
|
||||
val targetBlock = continueBlocks[targetStatement]!!
|
||||
referencedBlocks += targetBlock
|
||||
currentStatements += stateAndJump(targetBlock)
|
||||
}
|
||||
else {
|
||||
currentStatements += x
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTry(x: JsTry) = splitIfNecessary(x) {
|
||||
val catch = x.catches.firstOrNull()
|
||||
val successor = CoroutineBlock()
|
||||
|
||||
val catchBlock = CoroutineBlock()
|
||||
val oldCatchBlock = currentCatchBlock
|
||||
if (catch != null) {
|
||||
currentCatchBlock = catchBlock
|
||||
}
|
||||
|
||||
x.tryBlock.statements.forEach { it.accept(this) }
|
||||
currentBlock.statements += stateAndJump(successor)
|
||||
|
||||
currentCatchBlock = oldCatchBlock
|
||||
|
||||
if (catch != null) {
|
||||
currentBlock = catchBlock
|
||||
currentBlock.statements += JsAstUtils.newVar(catch.parameter.name, JsNameRef(resultFieldName, JsLiteral.THIS))
|
||||
|
||||
catch.body.statements.forEach { it.accept(this) }
|
||||
currentBlock.statements += stateAndJump(successor)
|
||||
}
|
||||
|
||||
currentBlock = successor
|
||||
}
|
||||
|
||||
override fun visitExpressionStatement(x: JsExpressionStatement) {
|
||||
val expression = x.expression
|
||||
if (expression is JsInvocation && expression.isSuspend) {
|
||||
handleSuspend(expression)
|
||||
}
|
||||
else {
|
||||
val splitExpression = handleExpression(x.expression)
|
||||
currentStatements += if (splitExpression == expression) x else JsExpressionStatement(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVars(x: JsVars) {
|
||||
super.visitVars(x)
|
||||
currentStatements += x
|
||||
}
|
||||
|
||||
override fun visit(x: JsVars.JsVar) {
|
||||
val initExpression = x.initExpression
|
||||
if (initExpression != null) {
|
||||
x.initExpression = handleExpression(initExpression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitReturn(x: JsReturn) {
|
||||
val returnExpression = x.expression
|
||||
if (returnExpression != null) {
|
||||
x.expression = handleExpression(returnExpression)
|
||||
}
|
||||
currentStatements += x
|
||||
}
|
||||
|
||||
override fun visitThrow(x: JsThrow) {
|
||||
if (throwFunctionName != null) {
|
||||
val exception = handleExpression(x.expression)
|
||||
val methodRef = JsNameRef(throwFunctionName, JsNameRef(controllerFieldName, JsLiteral.THIS))
|
||||
val invocation = JsInvocation(methodRef, exception).apply {
|
||||
source = x.source
|
||||
}
|
||||
currentStatements += JsReturn(invocation)
|
||||
}
|
||||
else {
|
||||
currentStatements += x
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleExpression(expression: JsExpression): JsExpression {
|
||||
if (expression !in nodesToSplit) return expression
|
||||
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun endVisit(x: JsInvocation, ctx: JsContext<in JsExpression>) {
|
||||
if (x.isSuspend) {
|
||||
ctx.replaceMe(handleSuspend(x))
|
||||
}
|
||||
super.endVisit(x, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return visitor.accept(expression)
|
||||
}
|
||||
|
||||
private fun handleSuspend(invocation: JsInvocation): JsExpression {
|
||||
val methodRef = invocation.qualifier as JsNameRef
|
||||
methodRef.qualifier = JsNameRef(controllerFieldName, methodRef.qualifier)
|
||||
|
||||
invocation.arguments += JsLiteral.THIS
|
||||
val nextBlock = CoroutineBlock()
|
||||
currentStatements += state(nextBlock)
|
||||
currentStatements += JsReturn(invocation)
|
||||
currentBlock = nextBlock
|
||||
|
||||
return JsNameRef(resultFieldName, JsLiteral.THIS)
|
||||
}
|
||||
|
||||
private fun state(target: CoroutineBlock): List<JsStatement> {
|
||||
val nextPlaceholder = JsDebugger()
|
||||
nextPlaceholder.targetBlock = target
|
||||
|
||||
val exceptionPlaceholder = JsDebugger()
|
||||
exceptionPlaceholder.targetExceptionBlock = currentCatchBlock
|
||||
|
||||
return listOf(nextPlaceholder, exceptionPlaceholder)
|
||||
}
|
||||
|
||||
private fun jump() = JsContinue()
|
||||
|
||||
private fun stateAndJump(target: CoroutineBlock): List<JsStatement> {
|
||||
return state(target) + jump()
|
||||
}
|
||||
|
||||
private inline fun splitIfNecessary(statement: JsStatement, action: () -> Unit) {
|
||||
if (statement in nodesToSplit) {
|
||||
action()
|
||||
}
|
||||
else {
|
||||
currentStatements += statement
|
||||
}
|
||||
}
|
||||
|
||||
private fun withBreakAndContinue(
|
||||
label: JsName?,
|
||||
statement: JsStatement,
|
||||
breakBlock: CoroutineBlock,
|
||||
continueBlock: CoroutineBlock? = null,
|
||||
action: () -> Unit
|
||||
) {
|
||||
breakBlocks[statement] = breakBlock
|
||||
if (continueBlock != null) {
|
||||
continueBlocks[statement] = continueBlock
|
||||
}
|
||||
|
||||
val oldDefaultBreakTarget = defaultBreakTarget
|
||||
val oldDefaultContinueTarget = defaultContinueTarget
|
||||
val (oldBreakTarget, oldContinueTarget) = if (label != null) {
|
||||
Pair(breakTargets[label], continueTargets[label])
|
||||
}
|
||||
else {
|
||||
Pair(null, null)
|
||||
}
|
||||
|
||||
defaultBreakTarget = statement
|
||||
if (label != null) {
|
||||
breakTargets[label] = statement
|
||||
if (continueBlock != null) {
|
||||
continueTargets[label] = statement
|
||||
}
|
||||
}
|
||||
if (continueBlock != null) {
|
||||
defaultContinueTarget = statement
|
||||
}
|
||||
|
||||
action()
|
||||
|
||||
defaultBreakTarget = oldDefaultBreakTarget
|
||||
defaultContinueTarget = oldDefaultContinueTarget
|
||||
if (label != null) {
|
||||
if (oldBreakTarget == null) {
|
||||
breakTargets.keys -= label
|
||||
}
|
||||
else {
|
||||
breakTargets[label] = oldBreakTarget
|
||||
}
|
||||
if (oldContinueTarget == null) {
|
||||
continueTargets.keys -= label
|
||||
}
|
||||
else {
|
||||
continueTargets[label] = oldContinueTarget
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineBlock.collectTargetBlocks(): Set<CoroutineBlock> {
|
||||
val targetBlocks = mutableSetOf<CoroutineBlock>()
|
||||
jsBlock.accept(object : RecursiveJsVisitor() {
|
||||
override fun visitDebugger(x: JsDebugger) {
|
||||
targetBlocks += x.targetExceptionBlock.singletonOrEmptyList() + x.targetBlock.singletonOrEmptyList()
|
||||
}
|
||||
})
|
||||
return targetBlocks
|
||||
}
|
||||
|
||||
private var JsDebugger.targetBlock: CoroutineBlock? by MetadataProperty(default = null)
|
||||
private var JsDebugger.targetExceptionBlock: CoroutineBlock? by MetadataProperty(default = null)
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.coroutine
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.isHandleResult
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.isSuspend
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.inline.ExpressionDecomposer
|
||||
import org.jetbrains.kotlin.js.inline.clean.FunctionPostProcessor
|
||||
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
|
||||
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
|
||||
class CoroutineFunctionTransformer(
|
||||
private val program: JsProgram,
|
||||
private val function: JsFunction,
|
||||
private val coroutineType: ClassDescriptor,
|
||||
private val controllerType: ClassDescriptor
|
||||
) {
|
||||
private val innerFunction = function.getInnerFunction()
|
||||
private val functionWithBody = innerFunction ?: function
|
||||
private val body = functionWithBody.body
|
||||
private val localVariables = (function.collectLocalVariables() + functionWithBody.collectLocalVariables()).toMutableSet()
|
||||
private val nameSuggestion = NameSuggestion()
|
||||
private val className = function.scope.parent.declareFreshName("Coroutine\$${function.name}")
|
||||
|
||||
fun transform(): List<JsStatement> {
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun <T : JsNode?> doTraverse(node: T, ctx: JsContext<in JsStatement>) {
|
||||
super.doTraverse(node, ctx)
|
||||
if (node is JsStatement) {
|
||||
val statements = ExpressionDecomposer.preserveEvaluationOrder(function.scope, node) {
|
||||
it is JsInvocation && it.isSuspend
|
||||
}
|
||||
ctx.addPrevious(statements)
|
||||
}
|
||||
}
|
||||
}
|
||||
visitor.accept(body)
|
||||
|
||||
val throwFunction = controllerType.findFunction("handleException")
|
||||
val throwName = throwFunction?.let {
|
||||
val throwId = nameSuggestion.suggest(it)!!.names.last()
|
||||
function.scope.declareName(throwId)
|
||||
}
|
||||
|
||||
val bodyTransformer = CoroutineBodyTransformer(program, function.scope, throwName)
|
||||
bodyTransformer.preProcess(body)
|
||||
body.statements.forEach { it.accept(bodyTransformer) }
|
||||
val coroutineBlocks = bodyTransformer.postProcess()
|
||||
|
||||
for (coroutineBlock in coroutineBlocks) {
|
||||
coroutineBlock.jsBlock.replaceLocalVariables(function.scope)
|
||||
coroutineBlock.jsBlock.replaceHandleResult(bodyTransformer)
|
||||
}
|
||||
|
||||
val additionalStatements = mutableListOf<JsStatement>()
|
||||
val resumeName = generateDoResume(coroutineBlocks, bodyTransformer, additionalStatements, throwName)
|
||||
generateContinuationConstructor(bodyTransformer, additionalStatements)
|
||||
generateContinuationMethods(resumeName, additionalStatements)
|
||||
|
||||
generateCoroutineInstantiation()
|
||||
|
||||
return additionalStatements
|
||||
}
|
||||
|
||||
private fun generateContinuationConstructor(bodyTransformer: CoroutineBodyTransformer,statements: MutableList<JsStatement>) {
|
||||
val constructor = JsFunction(function.scope.parent, JsBlock(), "Continuation")
|
||||
constructor.name = className
|
||||
constructor.parameters += function.parameters.map { JsParameter(it.name) }
|
||||
if (innerFunction != null) {
|
||||
constructor.parameters += innerFunction.parameters.map { JsParameter(it.name) }
|
||||
}
|
||||
|
||||
val controllerName = function.scope.declareFreshName("controller")
|
||||
constructor.parameters += JsParameter(controllerName)
|
||||
|
||||
val parameterNames = (function.parameters.map { it.name } + innerFunction?.parameters?.map { it.name }.orEmpty()).toSet()
|
||||
|
||||
constructor.body.statements.run {
|
||||
assign(bodyTransformer.stateFieldName, program.getNumberLiteral(0))
|
||||
assign(bodyTransformer.controllerFieldName, controllerName.makeRef())
|
||||
for (localVariable in localVariables) {
|
||||
val value = if (localVariable !in parameterNames) JsLiteral.NULL else localVariable.makeRef()
|
||||
assign(function.scope.getFieldName(localVariable), value)
|
||||
}
|
||||
}
|
||||
|
||||
statements.add(0, constructor.makeStmt())
|
||||
}
|
||||
|
||||
private fun generateContinuationMethods(doResumeName: JsName, statements: MutableList<JsStatement>) {
|
||||
generateResumeFunction(doResumeName, statements, "resume", "data", listOf())
|
||||
generateResumeFunction(doResumeName, statements, "resumeWithException", "exception", listOf(Namer.getUndefinedExpression()))
|
||||
}
|
||||
|
||||
private fun generateResumeFunction(
|
||||
doResumeName: JsName,
|
||||
statements: MutableList<JsStatement>,
|
||||
name: String,
|
||||
parameterName: String,
|
||||
additionalArgs: List<JsExpression>
|
||||
) {
|
||||
val resumeDescriptor = coroutineType.findFunction(name)!!
|
||||
val resumeId = nameSuggestion.suggest(resumeDescriptor)!!.names.last()
|
||||
val resumeName = function.scope.declareName(resumeId)
|
||||
|
||||
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), resumeDescriptor.toString())
|
||||
val resumeParameter = resumeFunction.scope.declareFreshName(parameterName)
|
||||
resumeFunction.parameters += JsParameter(resumeParameter)
|
||||
|
||||
resumeFunction.body.statements.apply {
|
||||
val invocation = JsInvocation(JsNameRef(doResumeName, JsLiteral.THIS), additionalArgs + resumeParameter.makeRef())
|
||||
this += JsReturn(invocation)
|
||||
}
|
||||
|
||||
statements.apply {
|
||||
assignToPrototype(resumeName, resumeFunction)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateDoResume(
|
||||
coroutineBlocks: List<CoroutineBlock>,
|
||||
bodyTransformer: CoroutineBodyTransformer,
|
||||
statements: MutableList<JsStatement>,
|
||||
throwName: JsName?
|
||||
): JsName {
|
||||
val resumeFunction = JsFunction(function.scope.parent, JsBlock(), "resume function")
|
||||
val resumeParameter = resumeFunction.scope.declareFreshName("data")
|
||||
val resumeException = resumeFunction.scope.declareFreshName("exception")
|
||||
resumeFunction.parameters += listOf(JsParameter(resumeParameter), JsParameter(resumeException))
|
||||
|
||||
val coroutineBody = generateCoroutineBody(bodyTransformer, coroutineBlocks, throwName, resumeException)
|
||||
functionWithBody.body.statements.clear()
|
||||
|
||||
resumeFunction.body.statements.apply {
|
||||
assign(bodyTransformer.resultFieldName, resumeParameter.makeRef())
|
||||
this += coroutineBody
|
||||
}
|
||||
|
||||
val resumeName = function.scope.parent.declareFreshName("doResume")
|
||||
statements.apply {
|
||||
assignToPrototype(resumeName, resumeFunction)
|
||||
}
|
||||
|
||||
FunctionPostProcessor(resumeFunction).apply()
|
||||
|
||||
return resumeName
|
||||
}
|
||||
|
||||
private fun generateCoroutineInstantiation() {
|
||||
val instantiation = JsNew(className.makeRef())
|
||||
instantiation.arguments += function.parameters.map { it.name.makeRef() }
|
||||
if (innerFunction != null) {
|
||||
instantiation.arguments += innerFunction.parameters.map { it.name.makeRef() }
|
||||
}
|
||||
|
||||
instantiation.arguments += JsLiteral.THIS
|
||||
|
||||
functionWithBody.body.statements += JsReturn(instantiation)
|
||||
}
|
||||
|
||||
private fun generateCoroutineBody(
|
||||
transformer: CoroutineBodyTransformer,
|
||||
blocks: List<CoroutineBlock>,
|
||||
throwName: JsName?,
|
||||
exceptionName: JsName
|
||||
): List<JsStatement> {
|
||||
val catch = JsCatch(functionWithBody.scope, "e")
|
||||
if (throwName != null) {
|
||||
val throwMethodRef = JsNameRef(throwName, JsNameRef(transformer.controllerFieldName, JsLiteral.THIS))
|
||||
catch.body = JsBlock(JsReturn(JsInvocation(throwMethodRef, catch.parameter.name.makeRef())))
|
||||
|
||||
val resultRef = JsNameRef(transformer.resultFieldName, JsLiteral.THIS)
|
||||
transformer.globalCatchBlock.statements += JsReturn(JsInvocation(throwMethodRef.deepCopy(), resultRef))
|
||||
}
|
||||
|
||||
val cases = blocks.withIndex().map { (index, block) ->
|
||||
JsCase().apply {
|
||||
caseExpression = program.getNumberLiteral(index)
|
||||
statements += block.statements
|
||||
}
|
||||
}
|
||||
val stateRef = JsNameRef(transformer.stateFieldName, JsLiteral.THIS)
|
||||
val switchStatement = JsSwitch(stateRef, cases)
|
||||
val loop = JsDoWhile(JsLiteral.TRUE, switchStatement)
|
||||
|
||||
val testExceptionPassed = JsAstUtils.notOptimized(
|
||||
JsAstUtils.typeOfIs(exceptionName.makeRef(), program.getStringLiteral("undefined")))
|
||||
val stateToException = JsAstUtils.assignment(
|
||||
JsNameRef(transformer.stateFieldName, JsLiteral.THIS),
|
||||
JsNameRef(transformer.exceptionStateName, JsLiteral.THIS))
|
||||
val exceptionToResult = JsAstUtils.assignment(JsNameRef(transformer.resultFieldName, JsLiteral.THIS), exceptionName.makeRef())
|
||||
val throwExceptionIfNeeded = JsIf(testExceptionPassed, JsBlock(stateToException.makeStmt(), exceptionToResult.makeStmt()))
|
||||
|
||||
val resultBlock = JsBlock(throwExceptionIfNeeded, loop)
|
||||
return if (throwName != null) listOf(JsTry(resultBlock, catch, null)) else resultBlock.statements
|
||||
}
|
||||
|
||||
private fun JsBlock.replaceHandleResult(transformer: CoroutineBodyTransformer) {
|
||||
accept(object : RecursiveJsVisitor() {
|
||||
override fun visitInvocation(invocation: JsInvocation) {
|
||||
super.visitInvocation(invocation)
|
||||
if (invocation.isHandleResult) {
|
||||
val methodRef = invocation.qualifier as JsNameRef
|
||||
methodRef.qualifier = JsNameRef(transformer.controllerFieldName, methodRef.qualifier)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun JsBlock.replaceLocalVariables(scope: JsScope) {
|
||||
accept(object : RecursiveJsVisitor() {
|
||||
override fun visit(x: JsVars.JsVar) {
|
||||
super.visit(x)
|
||||
localVariables += x.name
|
||||
}
|
||||
})
|
||||
|
||||
val visitor = object : JsVisitorWithContextImpl() {
|
||||
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) {
|
||||
if (x.qualifier == null && x.name in localVariables) {
|
||||
val fieldName = scope.getFieldName(x.name!!)
|
||||
ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS))
|
||||
}
|
||||
}
|
||||
|
||||
override fun endVisit(x: JsVars, ctx: JsContext<in JsStatement>) {
|
||||
val declaredNames = x.vars.map { it.name }
|
||||
val totalCount = declaredNames.size
|
||||
val localVarCount = declaredNames.count()
|
||||
|
||||
when {
|
||||
totalCount == localVarCount -> {
|
||||
val assignments = x.vars.mapNotNull {
|
||||
val fieldName = scope.getFieldName(it.name)
|
||||
val initExpression = it.initExpression
|
||||
if (initExpression != null) {
|
||||
JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), it.initExpression)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
if (assignments.isNotEmpty()) {
|
||||
ctx.replaceMe(JsExpressionStatement(JsAstUtils.newSequence(assignments)))
|
||||
}
|
||||
else {
|
||||
ctx.removeMe()
|
||||
}
|
||||
}
|
||||
localVarCount > 0 -> {
|
||||
for (declaration in x.vars) {
|
||||
if (declaration.name in localVariables) {
|
||||
val fieldName = scope.getFieldName(declaration.name)
|
||||
val assignment = JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), declaration.initExpression)
|
||||
ctx.addPrevious(assignment.makeStmt())
|
||||
}
|
||||
else {
|
||||
ctx.addPrevious(JsVars(declaration))
|
||||
}
|
||||
}
|
||||
ctx.removeMe()
|
||||
}
|
||||
}
|
||||
super.endVisit(x, ctx)
|
||||
}
|
||||
}
|
||||
visitor.accept(this)
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.findFunction(name: String): FunctionDescriptor? {
|
||||
val functions = unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.FUNCTIONS)
|
||||
.filter { it.name.asString() == name }
|
||||
return functions.mapNotNull { it as? FunctionDescriptor }.firstOrNull { it.kind.isReal }
|
||||
}
|
||||
|
||||
private fun JsScope.getFieldName(variableName: JsName) = declareName("local\$${variableName.ident}")
|
||||
|
||||
private fun MutableList<JsStatement>.assign(fieldName: JsName, value: JsExpression) {
|
||||
this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).makeStmt()
|
||||
}
|
||||
|
||||
private fun MutableList<JsStatement>.assignToPrototype(fieldName: JsName, value: JsExpression) {
|
||||
this += JsAstUtils.assignment(JsNameRef(fieldName, JsAstUtils.prototypeOf(className.makeRef())), value).makeStmt()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.coroutine
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.controllerType
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.coroutineType
|
||||
|
||||
class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContextImpl() {
|
||||
private val additionalStatements = mutableListOf<JsStatement>()
|
||||
|
||||
override fun endVisit(x: JsExpressionStatement, ctx: JsContext<in JsStatement>) {
|
||||
additionalStatements.forEach { ctx.addNext(it) }
|
||||
additionalStatements.clear()
|
||||
super.endVisit(x, ctx)
|
||||
}
|
||||
|
||||
override fun endVisit(x: JsFunction, ctx: JsContext<in JsStatement>) {
|
||||
val coroutineType = x.coroutineType
|
||||
if (coroutineType != null) {
|
||||
additionalStatements += CoroutineFunctionTransformer(program, x, coroutineType, x.controllerType!!).transform()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -455,7 +455,7 @@ internal open class JsExpressionVisitor() : JsVisitorWithContextImpl() {
|
||||
/**
|
||||
* Returns descendants of receiver, matched by [predicate].
|
||||
*/
|
||||
private fun JsNode.match(predicate: (JsNode) -> Boolean): Set<JsNode> {
|
||||
fun JsNode.match(predicate: (JsNode) -> Boolean): Set<JsNode> {
|
||||
val visitor = object : JsExpressionVisitor() {
|
||||
val matched = IdentitySet<JsNode>()
|
||||
|
||||
@@ -475,7 +475,7 @@ private fun JsNode.match(predicate: (JsNode) -> Boolean): Set<JsNode> {
|
||||
/**
|
||||
* Returns set of nodes, that satisfy transitive closure of `is parent` relation, starting from [nodes].
|
||||
*/
|
||||
private fun JsNode.withParentsOfNodes(nodes: Set<JsNode>): Set<JsNode> {
|
||||
fun JsNode.withParentsOfNodes(nodes: Set<JsNode>): Set<JsNode> {
|
||||
val visitor = object : JsExpressionVisitor() {
|
||||
private val stack = SmartList<JsNode>()
|
||||
val matched = IdentitySet<JsNode>()
|
||||
|
||||
@@ -112,7 +112,7 @@ internal class RedundantLabelRemoval(private val root: JsStatement) {
|
||||
hasChanges = true
|
||||
statement.thenStatement = statement.elseStatement
|
||||
statement.elseStatement = null
|
||||
statement.ifExpression = JsAstUtils.negated(statement.ifExpression)
|
||||
statement.ifExpression = JsAstUtils.not(statement.ifExpression)
|
||||
statement
|
||||
}
|
||||
else -> statement
|
||||
|
||||
@@ -141,9 +141,9 @@ class WhileConditionFolding(val body: JsBlock) {
|
||||
val result: JsExpression? = when (nextCondition) {
|
||||
// Just a little optimization. When inner statement is a single `break`, `nextCondition` would be false.
|
||||
// However, `A || false` can be rewritten as simply `A`
|
||||
JsLiteral.FALSE -> JsAstUtils.negatedOptimized(statement.ifExpression)
|
||||
JsLiteral.FALSE -> JsAstUtils.notOptimized(statement.ifExpression)
|
||||
null -> null
|
||||
else -> JsAstUtils.or(JsAstUtils.negatedOptimized(statement.ifExpression), nextCondition)
|
||||
else -> JsAstUtils.or(JsAstUtils.notOptimized(statement.ifExpression), nextCondition)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
+57
-399
@@ -5609,565 +5609,283 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("beginWithException.kt")
|
||||
public void testBeginWithException() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithException.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("beginWithExceptionNoHandleException.kt")
|
||||
public void testBeginWithExceptionNoHandleException() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/beginWithExceptionNoHandleException.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("coercionToUnit.kt")
|
||||
public void testCoercionToUnit() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/coercionToUnit.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("controllerAccessFromInnerLambda.kt")
|
||||
public void testControllerAccessFromInnerLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/controllerAccessFromInnerLambda.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("defaultParametersInSuspend.kt")
|
||||
public void testDefaultParametersInSuspend() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("emptyClosure.kt")
|
||||
public void testEmptyClosure() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/emptyClosure.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("falseUnitCoercion.kt")
|
||||
public void testFalseUnitCoercion() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/falseUnitCoercion.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("generate.kt")
|
||||
public void testGenerate() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/generate.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("handleException.kt")
|
||||
public void testHandleException() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/handleException.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("handleResultCallEmptyBody.kt")
|
||||
public void testHandleResultCallEmptyBody() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/handleResultCallEmptyBody.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("handleResultNonUnitExpression.kt")
|
||||
public void testHandleResultNonUnitExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/handleResultNonUnitExpression.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalState.kt")
|
||||
public void testIllegalState() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/illegalState.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineSuspendFunction.kt")
|
||||
public void testInlineSuspendFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlineSuspendFunction.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlinedTryCatchFinally.kt")
|
||||
public void testInlinedTryCatchFinally() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/inlinedTryCatchFinally.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("innerSuspensionCalls.kt")
|
||||
public void testInnerSuspensionCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/innerSuspensionCalls.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("iterateOverArray.kt")
|
||||
public void testIterateOverArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/iterateOverArray.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt12958.kt")
|
||||
public void testKt12958() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/kt12958.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaParameters.kt")
|
||||
public void testLambdaParameters() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lambdaParameters.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastExpressionIsLoop.kt")
|
||||
public void testLastExpressionIsLoop() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastStatementInc.kt")
|
||||
public void testLastStatementInc() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastStatementInc.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastStementAssignment.kt")
|
||||
public void testLastStementAssignment() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastStementAssignment.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lastUnitExpression.kt")
|
||||
public void testLastUnitExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/lastUnitExpression.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("manualContinuationImpl.kt")
|
||||
public void testManualContinuationImpl() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/manualContinuationImpl.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInvokeCalls.kt")
|
||||
public void testMultipleInvokeCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCalls.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInvokeCallsInsideInlineLambda1.kt")
|
||||
public void testMultipleInvokeCallsInsideInlineLambda1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda1.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInvokeCallsInsideInlineLambda2.kt")
|
||||
public void testMultipleInvokeCallsInsideInlineLambda2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda2.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("multipleInvokeCallsInsideInlineLambda3.kt")
|
||||
public void testMultipleInvokeCallsInsideInlineLambda3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda3.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nestedTryCatch.kt")
|
||||
public void testNestedTryCatch() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/nestedTryCatch.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noSuspensionPoints.kt")
|
||||
public void testNoSuspensionPoints() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonLocalReturnFromInlineLambda.kt")
|
||||
public void testNonLocalReturnFromInlineLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambda.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nonLocalReturnFromInlineLambdaDeep.kt")
|
||||
public void testNonLocalReturnFromInlineLambdaDeep() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/nonLocalReturnFromInlineLambdaDeep.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("returnByLabel.kt")
|
||||
public void testReturnByLabel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/returnByLabel.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simple.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleException.kt")
|
||||
public void testSimpleException() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simpleException.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("simpleWithHandleResult.kt")
|
||||
public void testSimpleWithHandleResult() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/simpleWithHandleResult.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("statementLikeLastExpression.kt")
|
||||
public void testStatementLikeLastExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/statementLikeLastExpression.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendDelegation.kt")
|
||||
public void testSuspendDelegation() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDelegation.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendExtension.kt")
|
||||
public void testSuspendExtension() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendExtension.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendFromInlineLambda.kt")
|
||||
public void testSuspendFromInlineLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFromInlineLambda.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendInCycle.kt")
|
||||
public void testSuspendInCycle() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendInCycle.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendInTheMiddleOfObjectConstruction.kt")
|
||||
public void testSuspendInTheMiddleOfObjectConstruction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendInTheMiddleOfObjectConstruction.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryCatchFinallyWithHandleResult.kt")
|
||||
public void testTryCatchFinallyWithHandleResult() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tryCatchFinallyWithHandleResult.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryCatchWithHandleResult.kt")
|
||||
public void testTryCatchWithHandleResult() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tryCatchWithHandleResult.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryFinallyInsideInlineLambda.kt")
|
||||
public void testTryFinallyInsideInlineLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tryFinallyInsideInlineLambda.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("tryFinallyWithHandleResult.kt")
|
||||
public void testTryFinallyWithHandleResult() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/tryFinallyWithHandleResult.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("varValueConflictsWithTable.kt")
|
||||
public void testVarValueConflictsWithTable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varValueConflictsWithTable.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("varValueConflictsWithTableSameSort.kt")
|
||||
public void testVarValueConflictsWithTableSameSort() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varValueConflictsWithTableSameSort.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling")
|
||||
@@ -6181,121 +5899,61 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("complicatedMerge.kt")
|
||||
public void testComplicatedMerge() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("i2bResult.kt")
|
||||
public void testI2bResult() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("loadFromBooleanArray.kt")
|
||||
public void testLoadFromBooleanArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("loadFromByteArray.kt")
|
||||
public void testLoadFromByteArray() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noVariableInTable.kt")
|
||||
public void testNoVariableInTable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sameIconst1ManyVars.kt")
|
||||
public void testSameIconst1ManyVars() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("usedInArrayStore.kt")
|
||||
public void testUsedInArrayStore() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("usedInMethodCall.kt")
|
||||
public void testUsedInMethodCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("usedInPutfield.kt")
|
||||
public void testUsedInPutfield() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("usedInVarStore.kt")
|
||||
public void testUsedInVarStore() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
|
||||
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS;
|
||||
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult;
|
||||
import org.jetbrains.kotlin.js.config.JsConfig;
|
||||
import org.jetbrains.kotlin.js.coroutine.CoroutineTransformer;
|
||||
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
|
||||
import org.jetbrains.kotlin.js.inline.JsInliner;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
@@ -85,6 +86,11 @@ public final class K2JSTranslator {
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
||||
|
||||
CoroutineTransformer coroutineTransformer = new CoroutineTransformer(program);
|
||||
coroutineTransformer.accept(program);
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
|
||||
|
||||
expandIsCalls(program, context);
|
||||
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
|
||||
|
||||
|
||||
+8
-1
@@ -17,6 +17,9 @@
|
||||
package org.jetbrains.kotlin.js.translate.callTranslator
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression
|
||||
import com.google.dart.compiler.backend.js.ast.JsFunction
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.isSuspend
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
@@ -115,7 +118,11 @@ private fun translateFunctionCall(context: TranslationContext,
|
||||
resolvedCall: ResolvedCall<out FunctionDescriptor>,
|
||||
explicitReceivers: ExplicitReceivers
|
||||
): JsExpression {
|
||||
return context.getCallInfo(resolvedCall, explicitReceivers).translateFunctionCall()
|
||||
val callExpression = context.getCallInfo(resolvedCall, explicitReceivers).translateFunctionCall()
|
||||
if (resolvedCall.resultingDescriptor.isSuspend) {
|
||||
(callExpression as JsInvocation).isSuspend = true
|
||||
}
|
||||
return callExpression
|
||||
}
|
||||
|
||||
fun computeExplicitReceiversForInvoke(
|
||||
|
||||
+12
-3
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention;
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator;
|
||||
@@ -37,6 +38,7 @@ import org.jetbrains.kotlin.js.translate.operation.UnaryOperationTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.reference.*;
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
@@ -44,6 +46,7 @@ import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
@@ -51,7 +54,9 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS;
|
||||
import static org.jetbrains.kotlin.js.translate.context.Namer.GET_KCLASS_FROM_EXPRESSION;
|
||||
@@ -129,7 +134,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
||||
}
|
||||
|
||||
JsReturn jsReturn;
|
||||
if (returned == null) {
|
||||
JsExpression handleResultExpression = TranslationUtils.tryTranslateHandleResult(context, jetReturnExpression, returned);
|
||||
if (handleResultExpression != null) {
|
||||
jsReturn = new JsReturn(handleResultExpression);
|
||||
}
|
||||
else if (returned == null) {
|
||||
jsReturn = new JsReturn(null);
|
||||
}
|
||||
else {
|
||||
@@ -445,13 +454,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
||||
@Override
|
||||
@NotNull
|
||||
public JsNode visitLambdaExpression(@NotNull KtLambdaExpression expression, @NotNull TranslationContext context) {
|
||||
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
|
||||
return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public JsNode visitNamedFunction(@NotNull KtNamedFunction expression, @NotNull TranslationContext context) {
|
||||
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
|
||||
JsExpression alias = new LiteralFunctionTranslator(context).translate(expression, null, null);
|
||||
|
||||
FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
|
||||
JsName name = context.getNameForDescriptor(descriptor);
|
||||
|
||||
+9
-1
@@ -36,7 +36,11 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
|
||||
class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslator(context) {
|
||||
fun translate(declaration: KtDeclarationWithBody): JsExpression {
|
||||
fun translate(
|
||||
declaration: KtDeclarationWithBody,
|
||||
continuationType: ClassDescriptor? = null,
|
||||
controllerType: ClassDescriptor? = null
|
||||
): JsExpression {
|
||||
val invokingContext = context()
|
||||
val descriptor = getFunctionDescriptor(invokingContext.bindingContext(), declaration)
|
||||
|
||||
@@ -69,6 +73,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
|
||||
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
|
||||
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
|
||||
lambdaCreator.isLocal = true
|
||||
lambdaCreator.coroutineType = continuationType
|
||||
lambdaCreator.controllerType = controllerType
|
||||
if (!isRecursive) {
|
||||
lambda.name = null
|
||||
}
|
||||
@@ -77,6 +83,8 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
|
||||
}
|
||||
|
||||
lambda.isLocal = true
|
||||
lambda.coroutineType = continuationType
|
||||
lambda.controllerType = controllerType
|
||||
|
||||
invokingContext.addDeclarationStatement(lambda.makeStmt())
|
||||
lambda.name.staticRef = lambda
|
||||
|
||||
+2
-2
@@ -56,7 +56,7 @@ import static org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt.getNameI
|
||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeByReference;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.equality;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
|
||||
import static org.jetbrains.kotlin.psi.KtPsiUtil.findChildByType;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.*;
|
||||
|
||||
@@ -112,7 +112,7 @@ public final class PatternTranslator extends AbstractTranslator {
|
||||
if (result == null) return JsLiteral.getBoolean(!expression.isNegated());
|
||||
|
||||
if (expression.isNegated()) {
|
||||
return negated(result);
|
||||
return not(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.KotlinType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
|
||||
|
||||
public final class WhenTranslator extends AbstractTranslator {
|
||||
@Nullable
|
||||
@@ -141,7 +141,7 @@ public final class WhenTranslator extends AbstractTranslator {
|
||||
private JsExpression translateCondition(@NotNull KtWhenCondition condition, @NotNull TranslationContext context) {
|
||||
JsExpression patternMatchExpression = translateWhenConditionToBooleanExpression(condition, context);
|
||||
if (isNegated(condition)) {
|
||||
return negated(patternMatchExpression);
|
||||
return not(patternMatchExpression);
|
||||
}
|
||||
return patternMatchExpression;
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
}
|
||||
|
||||
val result = TopLevelFIF.KOTLIN_EQUALS.apply(left, Arrays.asList<JsExpression>(right), context)
|
||||
return if (isNegated) JsAstUtils.negated(result) else result
|
||||
return if (isNegated) JsAstUtils.not(result) else result
|
||||
}
|
||||
|
||||
private fun canUseSimpleEquals(expression: KtBinaryExpression, context: TranslationContext): Boolean {
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ public class InOperationTranslator extends AbstractTranslator {
|
||||
private JsExpression translateGeneral(@NotNull ResolvedCall<? extends FunctionDescriptor> call, @NotNull JsExpression rightTranslated) {
|
||||
JsExpression result = CallTranslator.translate(context(), call, rightTranslated);
|
||||
if (negated) {
|
||||
result = JsAstUtils.negated(result);
|
||||
result = JsAstUtils.not(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+26
-8
@@ -20,10 +20,13 @@ import com.google.dart.compiler.backend.js.ast.*
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.sideEffects
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.expression.LiteralFunctionTranslator
|
||||
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator
|
||||
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
@@ -31,6 +34,8 @@ import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.getReferenceToJsClass
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -119,7 +124,7 @@ class CallArgumentTranslator private constructor(
|
||||
}
|
||||
}
|
||||
else {
|
||||
translateSingleArgument(actualArgument, result, argContext)
|
||||
translateSingleArgument(parameterDescriptor, actualArgument, result, argContext)
|
||||
}
|
||||
|
||||
context().moveVarsFrom(argContext)
|
||||
@@ -178,8 +183,12 @@ class CallArgumentTranslator private constructor(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>,
|
||||
context: TranslationContext) {
|
||||
private fun translateSingleArgument(
|
||||
parameterDescriptor: ValueParameterDescriptor,
|
||||
actualArgument: ResolvedValueArgument,
|
||||
result: MutableList<JsExpression>,
|
||||
context: TranslationContext
|
||||
) {
|
||||
val valueArguments = actualArgument.arguments
|
||||
|
||||
if (actualArgument is DefaultValueArgument) {
|
||||
@@ -190,10 +199,19 @@ class CallArgumentTranslator private constructor(
|
||||
assert(actualArgument is ExpressionValueArgument)
|
||||
assert(valueArguments.size == 1)
|
||||
|
||||
val argumentExpression = valueArguments[0].getArgumentExpression()!!
|
||||
val argumentExpression = KtPsiUtil.deparenthesize(valueArguments[0].getArgumentExpression())!!
|
||||
|
||||
val jsExpression = Translation.translateAsExpression(argumentExpression, context)
|
||||
result.add(jsExpression)
|
||||
result += if (parameterDescriptor.isCoroutine && argumentExpression is KtLambdaExpression) {
|
||||
val continuationType = parameterDescriptor.type.arguments.last().type
|
||||
val continuationDescriptor = continuationType.constructor.declarationDescriptor as ClassDescriptor
|
||||
val controllerType = parameterDescriptor.type.arguments[0].type
|
||||
val controllerDescriptor = controllerType.constructor.declarationDescriptor as ClassDescriptor
|
||||
LiteralFunctionTranslator(context).translate(
|
||||
argumentExpression.functionLiteral, continuationDescriptor, controllerDescriptor)
|
||||
}
|
||||
else {
|
||||
Translation.translateAsExpression(argumentExpression, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>,
|
||||
@@ -243,7 +261,7 @@ class CallArgumentTranslator private constructor(
|
||||
}
|
||||
|
||||
private fun concatArgumentsIfNeeded(concatArguments: List<JsExpression>): JsExpression {
|
||||
assert(concatArguments.size > 0) { "concatArguments.size should not be 0" }
|
||||
assert(concatArguments.isNotEmpty()) { "concatArguments.size should not be 0" }
|
||||
|
||||
if (concatArguments.size > 1) {
|
||||
return JsInvocation(JsNameRef("concat", concatArguments[0]), concatArguments.subList(1, concatArguments.size))
|
||||
@@ -255,7 +273,7 @@ class CallArgumentTranslator private constructor(
|
||||
}
|
||||
|
||||
private fun prepareConcatArguments(arguments: List<ValueArgument>, list: List<JsExpression>): MutableList<JsExpression> {
|
||||
assert(arguments.size != 0) { "arguments.size should not be 0" }
|
||||
assert(arguments.isNotEmpty()) { "arguments.size should not be 0" }
|
||||
assert(arguments.size == list.size) { "arguments.size: " + arguments.size + " != list.size: " + list.size }
|
||||
|
||||
val concatArguments = SmartList<JsExpression>()
|
||||
|
||||
+13
-1
@@ -94,7 +94,19 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
|
||||
jsBlock.getStatements().addAll(setDefaultValueForArguments(descriptor, context()));
|
||||
}
|
||||
|
||||
jsBlock.getStatements().addAll(mayBeWrapWithReturn(Translation.translateExpression(jetBodyExpression, context(), jsBlock)).getStatements());
|
||||
KotlinType returnType = descriptor.getReturnType();
|
||||
assert returnType != null;
|
||||
|
||||
|
||||
TranslationContext handleResultContext = context().innerBlock(jsBlock);
|
||||
JsExpression handleResultExpr = TranslationUtils.tryTranslateHandleResult(handleResultContext, declaration, jetBodyExpression);
|
||||
if (handleResultExpr != null) {
|
||||
jsBlock.getStatements().add(new JsReturn(handleResultExpr));
|
||||
}
|
||||
else {
|
||||
JsNode jsBody = Translation.translateExpression(jetBodyExpression, context(), jsBlock);
|
||||
jsBlock.getStatements().addAll(mayBeWrapWithReturn(jsBody).getStatements());
|
||||
}
|
||||
return jsBlock;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,12 +208,7 @@ public final class JsAstUtils {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsPrefixOperation negated(@NotNull JsExpression expression) {
|
||||
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsExpression negatedOptimized(@NotNull JsExpression expression) {
|
||||
public static JsExpression notOptimized(@NotNull JsExpression expression) {
|
||||
if (expression instanceof JsUnaryOperation) {
|
||||
JsUnaryOperation unary = (JsUnaryOperation) expression;
|
||||
if (unary.getOperator() == JsUnaryOperator.NOT) return unary.getArg();
|
||||
@@ -222,9 +217,9 @@ public final class JsAstUtils {
|
||||
JsBinaryOperation binary = (JsBinaryOperation) expression;
|
||||
switch (binary.getOperator()) {
|
||||
case AND:
|
||||
return or(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
|
||||
return or(notOptimized(binary.getArg1()), notOptimized(binary.getArg2()));
|
||||
case OR:
|
||||
return and(negatedOptimized(binary.getArg1()), negatedOptimized(binary.getArg2()));
|
||||
return and(notOptimized(binary.getArg1()), notOptimized(binary.getArg2()));
|
||||
case EQ:
|
||||
return new JsBinaryOperation(JsBinaryOperator.NEQ, binary.getArg1(), binary.getArg2());
|
||||
case NEQ:
|
||||
@@ -246,7 +241,7 @@ public final class JsAstUtils {
|
||||
}
|
||||
}
|
||||
|
||||
return negated(expression);
|
||||
return not(expression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -18,23 +18,30 @@ package org.jetbrains.kotlin.js.translate.utils;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
|
||||
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
|
||||
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
|
||||
@@ -289,4 +296,38 @@ public final class TranslationUtils {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JsExpression tryTranslateHandleResult(
|
||||
@NotNull TranslationContext context,
|
||||
@NotNull KtExpression expression,
|
||||
@Nullable KtExpression returnExpression
|
||||
) {
|
||||
ResolvedCall<? extends FunctionDescriptor> returnCall = context.bindingContext().get(
|
||||
BindingContext.RETURN_HANDLE_RESULT_RESOLVED_CALL, expression);
|
||||
if (returnCall == null) return null;
|
||||
|
||||
Map<KtExpression, JsExpression> aliases = new HashMap<KtExpression, JsExpression>();
|
||||
List<ResolvedValueArgument> arguments = returnCall.getValueArgumentsByIndex();
|
||||
assert arguments != null : "Arguments should be defined here: " + PsiUtilsKt.getTextWithLocation(expression);
|
||||
|
||||
KotlinType returnType = returnCall.getResultingDescriptor().getValueParameters().get(0).getType();
|
||||
|
||||
ValueArgument returnValueArgument = arguments.get(0).getArguments().get(0);
|
||||
if (returnExpression != null) {
|
||||
aliases.put(returnValueArgument.getArgumentExpression(), Translation.translateAsExpression(returnExpression, context));
|
||||
}
|
||||
else if (KotlinBuiltIns.isUnit(returnType)) {
|
||||
aliases.put(returnValueArgument.getArgumentExpression(), JsLiteral.NULL);
|
||||
}
|
||||
|
||||
ValueArgument continuationArgument = arguments.get(1).getArguments().get(0);
|
||||
aliases.put(continuationArgument.getArgumentExpression(), JsLiteral.THIS);
|
||||
|
||||
TranslationContext returnContext = context.innerContextWithAliasesForExpressions(aliases);
|
||||
JsInvocation handleResultInvocation = (JsInvocation) CallTranslator.translate(returnContext, returnCall, JsLiteral.THIS);
|
||||
MetadataProperties.setHandleResult(handleResultInvocation, true);
|
||||
|
||||
return handleResultInvocation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</fileset>
|
||||
|
||||
<fileset dir="${basedir}/core/builtins/src">
|
||||
<include name="kotlin/coroutines/**/*.kt"/>
|
||||
<include name="kotlin/reflect/**/*.kt"/>
|
||||
<include name="kotlin/annotation/Annotations.kt"/>
|
||||
<include name="kotlin/Unit.kt"/>
|
||||
|
||||
Reference in New Issue
Block a user