diff --git a/compiler/testData/codegen/boxWasmJsInterop/externals.kt b/compiler/testData/codegen/boxWasmJsInterop/externals.kt new file mode 100644 index 00000000000..47f1751f731 --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/externals.kt @@ -0,0 +1,25 @@ +// FILE: externals.js +function createObject() { + return {}; +} + +function setX(obj, x) { + obj.x = x; +} + +function getX(obj) { + return obj.x; +} + +// FILE: externals.kt +external interface Obj +external fun createObject(): Obj +external fun setX(obj: Obj, x: Int) +external fun getX(obj: Obj): Int + +fun box(): String { + val obj = createObject() + setX(obj, 100) + if (getX(obj) != 100) return "Fail 2" + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt b/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt new file mode 100644 index 00000000000..e381097831c --- /dev/null +++ b/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt @@ -0,0 +1,19 @@ +// MODULE: main +// FILE: externals.kt + +class C(val x: Int) + +@JsExport +fun makeC(x: Int): C = C(x) + +@JsExport +fun getX(c: C): Int = c.x + +fun box(): String = "OK" + +// FILE: jsExport__after.js + +const c = main.makeC(300); +if (main.getX(c) !== 300) { + throw "Fail 1"; +} \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt index 7830bbf2902..5c6f9543b29 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.js.test.es6.semantics.AbstractIrJsTypeScriptExportES import org.jetbrains.kotlin.js.test.ir.semantics.* import org.jetbrains.kotlin.js.test.semantics.* import org.jetbrains.kotlin.js.test.wasm.semantics.AbstractIrCodegenBoxWasmTest +import org.jetbrains.kotlin.js.test.wasm.semantics.AbstractIrCodegenWasmJsInteropWasmTest import org.jetbrains.kotlin.test.TargetBackend fun main(args: Array) { @@ -111,6 +112,14 @@ fun main(args: Array) { ) } + testClass { + model("codegen/boxWasmJsInterop", targetBackend = TargetBackend.WASM) + } + + testClass { + model("codegen/boxWasmJsInterop", targetBackend = TargetBackend.JS_IR) + } + testClass { model("codegen/box", targetBackend = TargetBackend.JS_IR_ES6, excludeDirs = jvmOnlyBoxTests) } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index ce237275470..c8d5290c276 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -257,14 +257,27 @@ abstract class BasicBoxTest( val additionalCommonFiles = additionalCommonFileDirectories.flatMap { baseDir -> JsTestUtils.getFilesInDirectoryByExtension(baseDir + "/", JavaScript.EXTENSION) } - val inputJsFiles = inputFiles - .filter { it.fileName.endsWith(".js") } - .map { inputJsFile -> - val sourceFile = File(inputJsFile.fileName) - val targetFile = File(outputDir, inputJsFile.module.outputFileSimpleName() + "-js-" + sourceFile.name) - FileUtil.copy(File(inputJsFile.fileName), targetFile) - targetFile.absolutePath + + val inputJsFilesBefore = mutableListOf() + val inputJsFilesAfter = mutableListOf() + + fun copyInputJsFile(inputJsFile: TestFile): String { + val sourceFile = File(inputJsFile.fileName) + val targetFile = File(outputDir, inputJsFile.module.outputFileSimpleName() + "-js-" + sourceFile.name) + FileUtil.copy(File(inputJsFile.fileName), targetFile) + return targetFile.absolutePath + } + + inputFiles.forEach { + val name = it.fileName + when { + name.endsWith("__after.js") -> + inputJsFilesAfter += copyInputJsFile(it) + + name.endsWith(".js") -> + inputJsFilesBefore += copyInputJsFile(it) } + } val additionalFiles = mutableListOf() @@ -288,23 +301,23 @@ abstract class BasicBoxTest( additionalMainFiles += additionalMainJsFile } - val allJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { it.first } + globalCommonFiles + localCommonFiles + - additionalCommonFiles + additionalMainFiles + val allJsFiles = additionalFiles + inputJsFilesBefore + generatedJsFiles.map { it.first } + globalCommonFiles + localCommonFiles + + additionalCommonFiles + additionalMainFiles + inputJsFilesAfter - val dceAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { + val dceAllJsFiles = additionalFiles + inputJsFilesBefore + generatedJsFiles.map { it.first.replace( outputDir.absolutePath, dceOutputDir.absolutePath ) - } + globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + } + globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + inputJsFilesAfter - val pirAllJsFiles = additionalFiles + inputJsFiles + generatedJsFiles.map { + val pirAllJsFiles = additionalFiles + inputJsFilesBefore + generatedJsFiles.map { it.first.replace( outputDir.absolutePath, pirOutputDir.absolutePath ) } + - globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + globalCommonFiles + localCommonFiles + additionalCommonFiles + additionalMainFiles + inputJsFilesAfter val dontRunGeneratedCode = diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt index 2fb278870df..45db2f9201f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.backend.js.MainModule import org.jetbrains.kotlin.ir.backend.js.ModulesStructure import org.jetbrains.kotlin.ir.backend.js.loadKlib import org.jetbrains.kotlin.ir.backend.js.prepareAnalyzedSourceModule +import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.facade.TranslationUnit @@ -65,11 +66,27 @@ abstract class BasicWasmBoxTest( val outputWatFile = outputFileBase + ".wat" val outputWasmFile = outputFileBase + ".wasm" val outputJsFile = outputFileBase + ".js" - val languageVersionSettings = inputFiles.mapNotNull { it.languageVersionSettings }.firstOrNull() - val kotlinFiles = inputFiles.filter { it.fileName.endsWith(".kt") } - val psiFiles = createPsiFiles(kotlinFiles.map { File(it.fileName).canonicalPath }.sorted()) + val kotlinFiles = mutableListOf() + val jsFilesBefore = mutableListOf() + val jsFilesAfter = mutableListOf() + + inputFiles.forEach { + val name = it.fileName + when { + name.endsWith(".kt") -> + kotlinFiles += name + + name.endsWith("__after.js") -> + jsFilesAfter += name + + name.endsWith(".js") -> + jsFilesBefore += name + } + } + + val psiFiles = createPsiFiles(kotlinFiles.map { File(it).canonicalPath }.sorted()) val config = createConfig(languageVersionSettings) translateFiles( file, @@ -87,7 +104,9 @@ abstract class BasicWasmBoxTest( "--experimental-wasm-typed-funcref", "--experimental-wasm-gc", "--experimental-wasm-eh", - outputJsFile + *jsFilesBefore.toTypedArray(), + outputJsFile, + *jsFilesAfter.toTypedArray(), ) } } @@ -162,6 +181,8 @@ abstract class BasicWasmBoxTest( const wasmBinary = read(String.raw`${outputWasmFile.absoluteFile}`, 'binary'); const wasmModule = new WebAssembly.Module(wasmBinary); wasmInstance = new WebAssembly.Instance(wasmModule, { runtime, js_code }); + + const ${sanitizeName(config.moduleId)} = wasmInstance.exports; const actualResult = importStringFromWasm(wasmInstance.exports.$testFunction()); if (actualResult !== "OK") @@ -215,7 +236,7 @@ abstract class BasicWasmBoxTest( companion object { const val TEST_DATA_DIR_PATH = "js/js.translator/testData/" - const val TEST_MODULE = "WASM_TESTS" + const val TEST_MODULE = "main" private const val TEST_FUNCTION = "box" } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt index ca733e9efaf..2ffb64a28eb 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt @@ -24,6 +24,12 @@ abstract class AbstractJsCodegenInlineTest : BasicBoxTest( "codegen/boxInline" ) +abstract class AbstractIrCodegenWasmJsInteropJsTest : BasicBoxTest( + "compiler/testData/codegen/wasmJsInterop", + "codegen/wasmJsInteropWasm" +) + + abstract class AbstractJsLegacyPrimitiveArraysBoxTest : BasicBoxTest( "compiler/testData/codegen/box/arrays/", "codegen/box/arrays-legacy-primitivearrays/", diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/abstractClassesForGeneratedWasmTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/abstractClassesForGeneratedWasmTests.kt index 85761c60c34..d1c16d1f632 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/abstractClassesForGeneratedWasmTests.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/wasm/semantics/abstractClassesForGeneratedWasmTests.kt @@ -10,4 +10,9 @@ import org.jetbrains.kotlin.js.test.BasicWasmBoxTest abstract class AbstractIrCodegenBoxWasmTest : BasicWasmBoxTest( "compiler/testData/codegen/box/", "codegen/wasmBox/" +) + +abstract class AbstractIrCodegenWasmJsInteropWasmTest : BasicWasmBoxTest( + "compiler/testData/codegen/wasmJsInterop", + "codegen/wasmJsInteropJs" ) \ No newline at end of file diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java new file mode 100644 index 00000000000..a5b348ed595 --- /dev/null +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.js.test.semantics; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/codegen/boxWasmJsInterop") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasmJsInteropJsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInBoxWasmJsInterop() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + + @TestMetadata("externals.kt") + public void testExternals() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt"); + } + + @TestMetadata("jsExport.kt") + public void testJsExport() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt"); + } +} diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java new file mode 100644 index 00000000000..31f3817edd2 --- /dev/null +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.js.test.wasm.semantics; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/codegen/boxWasmJsInterop") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWasmJsInteropWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInBoxWasmJsInterop() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true); + } + + @TestMetadata("externals.kt") + public void testExternals() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt"); + } + + @TestMetadata("jsExport.kt") + public void testJsExport() throws Exception { + runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt"); + } +}