diff --git a/build.gradle.kts b/build.gradle.kts index 9206e85..29040ac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.node.gradle) apply false alias(libs.plugins.kotlin.binary.compatibility.validator) apply false alias(libs.plugins.buildconfig) apply false } diff --git a/compiler-plugin/build.gradle.kts b/compiler-plugin/build.gradle.kts index 14c3d17..2913e26 100644 --- a/compiler-plugin/build.gradle.kts +++ b/compiler-plugin/build.gradle.kts @@ -1,10 +1,19 @@ +@file:OptIn(ExperimentalWasmDsl::class) + +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.targets.wasm.d8.D8EnvSpec +import org.jetbrains.kotlin.gradle.targets.wasm.d8.D8Plugin + plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.buildconfig) alias(libs.plugins.gradle.java.test.fixtures) + alias(libs.plugins.node.gradle) alias(libs.plugins.gradle.idea) } +project.plugins.apply(D8Plugin::class.java) + sourceSets { main { java.setSrcDirs(listOf("src")) @@ -32,6 +41,7 @@ dependencies { testFixturesApi(libs.kotlin.test.junit5) testFixturesApi(libs.kotlin.test.framework) testFixturesApi(libs.kotlin.compiler) + testFixturesRuntimeOnly(libs.junit) annotationsRuntimeClasspath(project(":plugin-annotations")) @@ -42,6 +52,9 @@ dependencies { testArtifacts(libs.kotlin.test) testArtifacts(libs.kotlin.script.runtime) testArtifacts(libs.kotlin.annotations.jvm) + + testArtifacts(libs.kotlin.stdlib.js) + testArtifacts(libs.kotlin.test.js) } buildConfig { @@ -71,6 +84,17 @@ tasks.test { systemProperty("idea.ignore.disabled.plugins", "true") systemProperty("idea.home.path", rootDir) + + // Properties required to run JS tests from the internal test framework. + val d8EnvSpec = project.the() + with(d8EnvSpec) { dependsOn(project.d8SetupTaskProvider) } + + setLibraryProperty("org.jetbrains.kotlin.test.kotlin-stdlib-js", "kotlin-stdlib-js") + setLibraryProperty("org.jetbrains.kotlin.test.kotlin-test-js", "kotlin-test-js") + + systemProperty("javascript.engine.path.V8", d8EnvSpec.executable.get()) + systemProperty("javascript.engine.path.repl", "${layout.projectDirectory.file("repl.js").asFile}") + systemProperty("kotlin.js.test.root.out.dir", "${layout.buildDirectory.get().asFile}/js-test-output") } kotlin { diff --git a/compiler-plugin/repl.js b/compiler-plugin/repl.js new file mode 100644 index 0000000..0990e71 --- /dev/null +++ b/compiler-plugin/repl.js @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2024 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. + */ + +// js/js.tests/test/org/jetbrains/kotlin/js/engine/repl.js + +/* +Some of non-standard APIs available in standalone JS engines: + + v8 sm jsc +load + + + load and evaluate a file +print + + + print to stdout +printErr + + + print to stderr +read + + + read a file as a text (v8, sm, jsc) or binary (sm, jsc) +readline + + + read line from stdin +readbuffer + - - read a binary file in v8 +quit + + + stop the process + +V8: +https://v8.dev/docs/d8 +https://github.com/v8/v8/blob/4b9b23521e6fd42373ebbcb20ebe03bf445494f9/src/d8.cc +https://riptutorial.com/v8/example/25393/useful-built-in-functions-and-objects-in-d8 + +SpiderMonkey: +https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Introduction_to_the_JavaScript_shell + +JavaScriptCore: +https://trac.webkit.org/wiki/JSC +https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/jsc.cpp +*/ + +/** + * @type {number} + */ +let currentRealmIndex = Realm.current(); + +function resetRealm() { + if (currentRealmIndex !== 0) Realm.dispose(currentRealmIndex); + currentRealmIndex = Realm.createAllowCrossRealmAccess() +} + +/** + * @type {?Map} + */ +let globalState = null; + +function saveGlobalState() { + globalState = new Map(); + const currentGlobal = Realm.global(currentRealmIndex) + for (const k in currentGlobal) { + globalState.set(k, currentGlobal[k]); + } + + console.log(Array.from(globalState.entries())) +} + +function restoreGlobalState() { + if (globalState === null) throw Error("There is no saved state!") + + const currentGlobal = Realm.global(currentRealmIndex) + + console.log(Array.from(globalState.entries())) + + for (const k in currentGlobal) { + let prev = globalState.get(k); + if (prev !== currentGlobal[k]) { + currentGlobal[k] = prev; + } + } + globalState = null; +} + +// To prevent accessing to current global state +resetRealm(); + +// noinspection InfiniteLoopJS +async function loop() { + while (true) { + let code = readline().replace(/\\n/g, '\n'); + + try { + switch (code) { + case "!reset": + resetRealm() + break; + case "!saveGlobalState": + saveGlobalState(); + break; + case "!restoreGlobalState": + restoreGlobalState(); + break; + default: + print(await Realm.eval(currentRealmIndex, code)); + } + } catch(e) { + printErr(e.stack != null ? e.stack : e.toString()); + printErr('\nCODE:\n' + code); + } + + print(''); + } +} + +loop() \ No newline at end of file diff --git a/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/GenerateTests.kt b/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/GenerateTests.kt index 751c9e3..b0efa32 100644 --- a/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/GenerateTests.kt +++ b/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/GenerateTests.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlin.compiler.plugin.template +import org.jetbrains.kotlin.compiler.plugin.template.runners.AbstractJsBoxTest import org.jetbrains.kotlin.compiler.plugin.template.runners.AbstractJvmBoxTest import org.jetbrains.kotlin.compiler.plugin.template.runners.AbstractJvmDiagnosticTest import org.jetbrains.kotlin.generators.dsl.junit5.generateTestGroupSuiteWithJUnit5 @@ -14,6 +15,10 @@ fun main() { testClass { model("box") } + + testClass { + model("box") + } } } } diff --git a/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/runners/AbstractJsBoxTest.kt b/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/runners/AbstractJsBoxTest.kt new file mode 100644 index 0000000..0a4bc66 --- /dev/null +++ b/compiler-plugin/test-fixtures/org/jetbrains/kotlin/compiler/plugin/template/runners/AbstractJsBoxTest.kt @@ -0,0 +1,44 @@ +package org.jetbrains.kotlin.compiler.plugin.template.runners + +import org.jetbrains.kotlin.compiler.plugin.template.services.configurePlugin +import org.jetbrains.kotlin.js.test.runners.AbstractJsTest +import org.jetbrains.kotlin.test.FirParser +import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder +import org.jetbrains.kotlin.test.directives.CodegenTestDirectives +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives +import org.jetbrains.kotlin.test.services.EnvironmentBasedStandardLibrariesPathProvider +import org.jetbrains.kotlin.test.services.KotlinStandardLibrariesPathProvider + +open class AbstractJsBoxTest : AbstractJsTest( + pathToTestDir = "compiler-plugin/testData/box", + testGroupOutputDirPrefix = "box/", + parser = FirParser.LightTree, +) { + override fun createKotlinStandardLibrariesPathProvider(): KotlinStandardLibrariesPathProvider { + return EnvironmentBasedStandardLibrariesPathProvider + } + + override fun configure(builder: TestConfigurationBuilder) { + super.configure(builder) + + with(builder) { + /* + * Containers of different directives, which can be used in tests: + * - ModuleStructureDirectives + * - LanguageSettingsDirectives + * - DiagnosticsDirectives + * - FirDiagnosticsDirectives + * - CodegenTestDirectives + * - JvmEnvironmentConfigurationDirectives + * + * All of them are located in `org.jetbrains.kotlin.test.directives` package + */ + defaultDirectives { + +CodegenTestDirectives.DUMP_IR + +FirDiagnosticsDirectives.FIR_DUMP + } + + configurePlugin() + } + } +} diff --git a/compiler-plugin/test-gen/org/jetbrains/kotlin/compiler/plugin/template/runners/JsBoxTestGenerated.java b/compiler-plugin/test-gen/org/jetbrains/kotlin/compiler/plugin/template/runners/JsBoxTestGenerated.java new file mode 100644 index 0000000..b9b034f --- /dev/null +++ b/compiler-plugin/test-gen/org/jetbrains/kotlin/compiler/plugin/template/runners/JsBoxTestGenerated.java @@ -0,0 +1,34 @@ + + +package org.jetbrains.kotlin.compiler.plugin.template.runners; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.compiler.plugin.template.GenerateTestsKt}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler-plugin/testData/box") +@TestDataPath("$PROJECT_ROOT") +public class JsBoxTestGenerated extends AbstractJsBoxTest { + @Test + public void testAllFilesPresentInBox() { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler-plugin/testData/box"), Pattern.compile("^(.+)\\.kt$"), null, true); + } + + @Test + @TestMetadata("anotherBoxTest.kt") + public void testAnotherBoxTest() { + runTest("compiler-plugin/testData/box/anotherBoxTest.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() { + runTest("compiler-plugin/testData/box/simple.kt"); + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5e3613e..dcf10c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,18 +1,24 @@ [versions] # https://github.com/JetBrains/kotlin -kotlin = "2.3.0" +kotlin = "2.3.20-RC" # https://github.com/Kotlin/binary-compatibility-validator kotlin-binaryCompatibilityValidator = "0.16.3" +# https://github.com/junit-team/junit4 +junit = "4.13.2" + # https://github.com/gmazzo/gradle-buildconfig-plugin buildconfig = "5.6.5" +node-gradle = "7.0.2" [libraries] kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlin-stdlib-js = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-js", version.ref = "kotlin" } kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } +kotlin-test-js = { group = "org.jetbrains.kotlin", name = "kotlin-test-js", version.ref = "kotlin" } kotlin-script-runtime = { group = "org.jetbrains.kotlin", name = "kotlin-script-runtime", version.ref = "kotlin" } kotlin-test-junit5 = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit5", version.ref = "kotlin" } kotlin-test-framework = { group = "org.jetbrains.kotlin", name = "kotlin-compiler-internal-test-framework", version.ref = "kotlin" } @@ -21,11 +27,14 @@ kotlin-compiler = { group = "org.jetbrains.kotlin", name = "kotlin-compiler", ve kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" } kotlin-gradle-plugin-api = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin-api", version.ref = "kotlin" } +junit = { module = "junit:junit", version.ref = "junit" } + [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlin-binaryCompatibilityValidator"} buildconfig = { id = "com.github.gmazzo.buildconfig", version.ref = "buildconfig"} +node-gradle = { id = "com.github.node-gradle.node", version.ref = "node-gradle" } gradle-java-test-fixtures = { id = "java-test-fixtures" } gradle-idea = { id = "idea" } diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index e35fa39..40a971f 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -218,6 +218,11 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -288,10 +293,10 @@ minimatch@^9.0.4, minimatch@^9.0.5: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mocha@11.7.2: - version "11.7.2" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.2.tgz#3c0079fe5cc2f8ea86d99124debcc42bb1ab22b5" - integrity sha512-lkqVJPmqqG/w5jmmFtiRvtA2jkDyNVUcefFJKb2uyX4dekk8Okgqop3cgbFiaIvj8uCRJVTP5x9dfxGyXm2jvQ== +mocha@11.7.5: + version "11.7.5" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" + integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== dependencies: browser-stdout "^1.3.1" chokidar "^4.0.1" @@ -301,6 +306,7 @@ mocha@11.7.2: find-up "^5.0.0" glob "^10.4.5" he "^1.2.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" log-symbols "^4.1.0" minimatch "^9.0.5"