JS: support [suspend] fun main([args: Array<String>]) (KT-26628, KT-26572 fixed)

This commit is contained in:
Anton Bannykh
2018-09-10 19:33:04 +03:00
committed by Denis Zharkov
parent 7e9704e50d
commit ead9b31e03
10 changed files with 226 additions and 3 deletions
@@ -102,6 +102,8 @@ abstract class BasicBoxTest(
fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage)
}
val actualMainCallParameters = if (CALL_MAIN_PATTERN.matcher(fileContent).find()) MainCallParameters.mainWithArguments(listOf("testArg")) else mainCallParameters
val outputPrefixFile = getOutputPrefixFile(filePath)
val outputPostfixFile = getOutputPostfixFile(filePath)
@@ -124,7 +126,7 @@ abstract class BasicBoxTest(
generateJavaScriptFile(
file.parent, module, outputFileName, dependencies, friends, modules.size > 1,
!SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(),
outputPrefixFile, outputPostfixFile, mainCallParameters, testPackage, testFunction
outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction
)
if (!module.name.endsWith(OLD_MODULE_SUFFIX)) Pair(outputFileName, module) else null
@@ -771,6 +773,7 @@ abstract class BasicBoxTest(
private val EXPECTED_REACHABLE_NODES = Pattern.compile("^// *$EXPECTED_REACHABLE_NODES_DIRECTIVE: *([0-9]+) *$", Pattern.MULTILINE)
private val RECOMPILE_PATTERN = Pattern.compile("^// *RECOMPILE *$", Pattern.MULTILINE)
private val SOURCE_MAP_SOURCE_EMBEDDING = Regex("^// *SOURCE_MAP_EMBED_SOURCES: ([A-Z]+)*\$", RegexOption.MULTILINE)
private val CALL_MAIN_PATTERN = Pattern.compile("^// *CALL_MAIN *$", Pattern.MULTILINE)
val TEST_MODULE = "JS_TESTS"
private val DEFAULT_MODULE = "main"
@@ -5493,6 +5493,44 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
}
}
@TestMetadata("js/js.translator/testData/box/main")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Main extends AbstractBoxJsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInMain() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/main"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/main/noArgs.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/main/simple.kt");
}
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMain.kt");
}
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMainNoArgs.kt");
}
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMainThrows.kt");
}
}
@TestMetadata("js/js.translator/testData/box/multiFile")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -5493,6 +5493,44 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
}
}
@TestMetadata("js/js.translator/testData/box/main")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Main extends AbstractIrBoxJsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInMain() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/main"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.JS_IR, true);
}
@TestMetadata("noArgs.kt")
public void testNoArgs() throws Exception {
runTest("js/js.translator/testData/box/main/noArgs.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("js/js.translator/testData/box/main/simple.kt");
}
@TestMetadata("suspendMain.kt")
public void testSuspendMain() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMain.kt");
}
@TestMetadata("suspendMainNoArgs.kt")
public void testSuspendMainNoArgs() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMainNoArgs.kt");
}
@TestMetadata("suspendMainThrows.kt")
public void testSuspendMainThrows() throws Exception {
runTest("js/js.translator/testData/box/main/suspendMainThrows.kt");
}
}
@TestMetadata("js/js.translator/testData/box/multiFile")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.idea.MainFunctionDetector;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
@@ -41,17 +42,22 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.FileDeclarationVisitor;
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor;
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator;
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
import org.jetbrains.kotlin.js.translate.utils.*;
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
@@ -468,8 +474,24 @@ public final class Translation {
if (functionDescriptor == null) {
return null;
}
JsArrayLiteral argument = new JsArrayLiteral(toStringLiteralList(arguments));
JsExpression call = CallTranslator.INSTANCE.buildCall(context, functionDescriptor, Collections.singletonList(argument), null);
int parameterCount = functionDescriptor.getValueParameters().size();
assert parameterCount <= 1;
List<JsExpression> args = new ArrayList<>();
if (parameterCount != 0) {
args.add(new JsArrayLiteral(toStringLiteralList(arguments)));
}
if (functionDescriptor.isSuspend()) {
MemberScope scope = moduleDescriptor.getPackage(new FqNameUnsafe("kotlin.coroutines.js.internal").toSafe()).getMemberScope();
PropertyDescriptor emptyContinuation = DescriptorUtils.getPropertyByName(scope, Name.identifier("EmptyContinuation"));
args.add(ReferenceTranslator.translateAsValueReference(emptyContinuation, context));
args.add(new JsBooleanLiteral(false));
}
JsExpression call = CallTranslator.INSTANCE.buildCall(context, functionDescriptor, args, null);
context.addTopLevelStatement(call.makeStmt());
return staticContext.getFragment();
}
+11
View File
@@ -0,0 +1,11 @@
// EXPECTED_REACHABLE_NODES: 1281
// IGNORE_BACKEND: JS_IR
// CALL_MAIN
var ok: String = "fail"
fun main() {
ok = "OK"
}
fun box() = ok
+14
View File
@@ -0,0 +1,14 @@
// EXPECTED_REACHABLE_NODES: 1281
// IGNORE_BACKEND: JS_IR
// CALL_MAIN
var ok: String = "fail"
fun main(args: Array<String>) {
assertEquals(1, args.size)
assertEquals("testArg", args[0])
ok = "OK"
}
fun box() = ok
+28
View File
@@ -0,0 +1,28 @@
// EXPECTED_REACHABLE_NODES: 1296
// IGNORE_BACKEND: JS_IR
// CALL_MAIN
import kotlin.coroutines.*
var ok: String = "fail"
var callback: () -> Unit = {}
suspend fun main(args: Array<String>) {
assertEquals(1, args.size)
assertEquals("testArg", args[0])
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
ok = "OK"
}
fun box(): String {
assertEquals("fail", ok)
callback()
return ok
}
+25
View File
@@ -0,0 +1,25 @@
// EXPECTED_REACHABLE_NODES: 1296
// IGNORE_BACKEND: JS_IR
// CALL_MAIN
import kotlin.coroutines.*
var ok: String = "fail"
var callback: () -> Unit = {}
suspend fun main() {
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
ok = "OK"
}
fun box(): String {
assertEquals("fail", ok)
callback()
return ok
}
+30
View File
@@ -0,0 +1,30 @@
// EXPECTED_REACHABLE_NODES: 1299
// IGNORE_BACKEND: JS_IR
// CALL_MAIN
import kotlin.coroutines.*
var callback: () -> Unit = {}
val exception = Exception()
suspend fun main() {
suspendCoroutine<Unit> { cont ->
callback = {
cont.resume(Unit)
}
}
throw exception
}
fun box(): String {
try {
callback()
} catch (e: Exception) {
assertTrue(e === exception)
}
return "OK"
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.js.internal
import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
@PublishedApi
internal val EmptyContinuation = Continuation<Any?>(EmptyCoroutineContext) { result ->
result.getOrThrow()
}