JS: prototyping coroutines

This commit is contained in:
Alexey Andreev
2016-10-28 20:15:13 +03:00
parent be8b1b7dfd
commit c5999e8375
81 changed files with 1037 additions and 606 deletions
@@ -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
// 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)
-3
View File
@@ -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>) {
@@ -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)
@@ -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)
@@ -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)
@@ -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
// 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")
-3
View File
@@ -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"
@@ -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
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
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>) {
@@ -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>) {
-3
View File
@@ -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>) {
@@ -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")
@@ -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")
@@ -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)
@@ -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
}
@@ -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();
@@ -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(
@@ -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);
@@ -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
@@ -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;
}
@@ -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;
}
@@ -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 {
@@ -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;
}
@@ -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>()
@@ -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;
}
}
+1
View File
@@ -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"/>