[JS IR] Enable DTS generation in IC
^KT-54398 Fixed
This commit is contained in:
committed by
Space Team
parent
d21cbfe02e
commit
9edaebf235
+7
-3
@@ -95,10 +95,14 @@ internal fun File.fileHashForIC(): ICHash {
|
||||
}
|
||||
|
||||
internal fun CompilerConfiguration.configHashForIC() = HashCalculatorForIC().apply {
|
||||
val importantBooleanSettingKeys = listOf(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION)
|
||||
updateForEach(importantBooleanSettingKeys) { key ->
|
||||
val importantSettings = listOf(
|
||||
JSConfigurationKeys.GENERATE_DTS,
|
||||
JSConfigurationKeys.MODULE_KIND,
|
||||
JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION
|
||||
)
|
||||
updateForEach(importantSettings) { key ->
|
||||
update(key.toString())
|
||||
update(getBoolean(key).toString())
|
||||
update(get(key).toString())
|
||||
}
|
||||
|
||||
update(languageVersionSettings.toString())
|
||||
|
||||
+16
-10
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.ic
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.CompilationOutputs
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CrossModuleReferences
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrModuleHeader
|
||||
import java.io.File
|
||||
@@ -15,6 +16,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
private const val JS_MODULE_HEADER = "js.module.header.bin"
|
||||
private const val CACHED_MODULE_JS = "module.js"
|
||||
private const val CACHED_MODULE_JS_MAP = "module.js.map"
|
||||
private const val CACHED_MODULE_D_TS = "module.d.ts"
|
||||
}
|
||||
|
||||
private enum class NameType(val typeMask: Int) {
|
||||
@@ -71,22 +73,26 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.writeIfNotNull(data: String?) {
|
||||
if (data != null) {
|
||||
recreate()
|
||||
writeText(data)
|
||||
} else {
|
||||
ifExists { delete() }
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir ->
|
||||
val jsCode = File(cacheDir, CACHED_MODULE_JS).ifExists { readText() }
|
||||
val sourceMap = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { readText() }
|
||||
jsCode?.let { CompilationOutputs(it, null, null, sourceMap) }
|
||||
val tsDefinitions = File(cacheDir, CACHED_MODULE_D_TS).ifExists { TypeScriptFragment(readText()) }
|
||||
jsCode?.let { CompilationOutputs(it, tsDefinitions, null, sourceMap) }
|
||||
}
|
||||
|
||||
fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputs) = artifact.artifactsDir?.let { cacheDir ->
|
||||
val jsCodeCache = File(cacheDir, CACHED_MODULE_JS).apply { recreate() }
|
||||
jsCodeCache.writeText(compilationOutputs.jsCode)
|
||||
val jsMapCache = File(cacheDir, CACHED_MODULE_JS_MAP)
|
||||
if (compilationOutputs.sourceMap != null) {
|
||||
jsMapCache.recreate()
|
||||
jsMapCache.writeText(compilationOutputs.sourceMap)
|
||||
} else {
|
||||
jsMapCache.ifExists { delete() }
|
||||
}
|
||||
File(cacheDir, CACHED_MODULE_JS).writeIfNotNull(compilationOutputs.jsCode)
|
||||
File(cacheDir, CACHED_MODULE_JS_MAP).writeIfNotNull(compilationOutputs.sourceMap)
|
||||
File(cacheDir, CACHED_MODULE_D_TS).writeIfNotNull(compilationOutputs.tsDefinitions?.raw)
|
||||
}
|
||||
|
||||
fun loadProgramHeadersFromCache(): List<CachedModuleInfo> {
|
||||
|
||||
@@ -47,7 +47,8 @@ class ModuleInfo(val moduleName: String) {
|
||||
val id: Int,
|
||||
val dependencies: Collection<Dependency>,
|
||||
val modifications: List<Modification>,
|
||||
val expectedFileStats: Map<String, Set<String>>
|
||||
val expectedFileStats: Map<String, Set<String>>,
|
||||
val expectedDTS: Set<String>
|
||||
)
|
||||
|
||||
val steps = mutableListOf<ModuleStep>()
|
||||
@@ -65,6 +66,7 @@ private const val FRIENDS = "friends"
|
||||
private const val MODIFICATIONS = "modifications"
|
||||
private const val MODIFICATION_UPDATE = "U"
|
||||
private const val MODIFICATION_DELETE = "D"
|
||||
private const val EXPECTED_DTS_LIST = "expected dts"
|
||||
|
||||
private val STEP_PATTERN = Pattern.compile("^\\s*STEP\\s+(\\d+)\\.*(\\d+)?\\s*:?$")
|
||||
|
||||
@@ -210,6 +212,7 @@ class ModuleInfoParser(infoFile: File) : InfoParser<ModuleInfo>(infoFile) {
|
||||
val regularDependencies = mutableSetOf<String>()
|
||||
val friendDependencies = mutableSetOf<String>()
|
||||
val modifications = mutableListOf<ModuleInfo.Modification>()
|
||||
val expectedDTS = mutableSetOf<String>()
|
||||
|
||||
loop { line ->
|
||||
if (line.matches(STEP_PATTERN.toRegex()))
|
||||
@@ -230,6 +233,7 @@ class ModuleInfoParser(infoFile: File) : InfoParser<ModuleInfo>(infoFile) {
|
||||
DEPENDENCIES -> getOpArgs().forEach { regularDependencies += it }
|
||||
FRIENDS -> getOpArgs().forEach { friendDependencies += it }
|
||||
MODIFICATIONS -> modifications += parseModifications()
|
||||
EXPECTED_DTS_LIST -> getOpArgs().forEach { expectedDTS += it }
|
||||
else -> error(diagnosticMessage("Unknown op $op", line))
|
||||
}
|
||||
}
|
||||
@@ -250,7 +254,8 @@ class ModuleInfoParser(infoFile: File) : InfoParser<ModuleInfo>(infoFile) {
|
||||
id = it,
|
||||
dependencies = dependencies,
|
||||
modifications = modifications,
|
||||
expectedFileStats = expectedFileStats
|
||||
expectedFileStats = expectedFileStats,
|
||||
expectedDTS = expectedDTS
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.test.builders.LanguageVersionSettingsBuilder
|
||||
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
|
||||
import org.jetbrains.kotlin.test.util.JUnit4Assertions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
||||
import org.junit.ComparisonFailure
|
||||
import java.io.File
|
||||
import java.util.EnumSet
|
||||
@@ -49,6 +50,8 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
private const val STDLIB_MODULE_NAME = "kotlin-kotlin-stdlib-js-ir"
|
||||
|
||||
private val KT_FILE_IGNORE_PATTERN = Regex("^.*\\..+\\.kt$")
|
||||
|
||||
private val JS_MODULE_KIND = ModuleKind.PLAIN
|
||||
}
|
||||
|
||||
override fun createEnvironment(): KotlinCoreEnvironment {
|
||||
@@ -94,7 +97,8 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
private fun createConfiguration(moduleName: String, language: List<String>): CompilerConfiguration {
|
||||
val copy = environment.configuration.copy()
|
||||
copy.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
|
||||
copy.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
|
||||
copy.put(JSConfigurationKeys.GENERATE_DTS, true)
|
||||
copy.put(JSConfigurationKeys.MODULE_KIND, JS_MODULE_KIND)
|
||||
copy.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
|
||||
|
||||
copy.languageVersionSettings = with(LanguageVersionSettingsBuilder()) {
|
||||
@@ -123,7 +127,8 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
val moduleName: String,
|
||||
val modulePath: String,
|
||||
val friends: List<String>,
|
||||
val expectedFileStats: Map<String, Set<String>>
|
||||
val expectedFileStats: Map<String, Set<String>>,
|
||||
val expectedDTS: String?
|
||||
)
|
||||
|
||||
private fun setupTestStep(projStep: ProjectInfo.ProjectBuildStep, module: String, buildKlib: Boolean): TestStepInfo {
|
||||
@@ -158,11 +163,16 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
val configuration = createConfiguration(module, projStep.language)
|
||||
buildArtifact(configuration, module, moduleSourceDir, dependencies, friends, outputKlibFile)
|
||||
}
|
||||
|
||||
val dtsFile = moduleStep.expectedDTS.ifNotEmpty {
|
||||
moduleTestDir.resolve(singleOrNull() ?: error("$module module may generate only one d.ts at step $projStepId"))
|
||||
}
|
||||
return TestStepInfo(
|
||||
module.safeModuleName,
|
||||
outputKlibFile.canonicalPath,
|
||||
friends.map { it.canonicalPath },
|
||||
expectedFileStats
|
||||
expectedFileStats,
|
||||
dtsFile?.readText()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -232,6 +242,17 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyDTS(stepId: Int, testInfo: List<TestStepInfo>, jsOutput: CompilationOutputs) {
|
||||
for (info in testInfo) {
|
||||
val expectedDTS = info.expectedDTS ?: continue
|
||||
val output = jsOutput.dependencies.find { it.first == info.moduleName }?.second ?: jsOutput
|
||||
val gotDTS = output.getFullTsDefinition(info.moduleName, JS_MODULE_KIND)
|
||||
JUnit4Assertions.assertEquals(expectedDTS, gotDTS) {
|
||||
"Mismatched d.ts for module ${info.moduleName} at step $stepId"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun execute() {
|
||||
for (projStep in projectInfo.steps) {
|
||||
val testInfo = projStep.order.map { setupTestStep(projStep, it, true) }
|
||||
@@ -272,6 +293,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
|
||||
val (jsOutput, rebuiltModules) = jsExecutableProducer.buildExecutable(multiModule = true, outJsProgram = true)
|
||||
verifyJsExecutableProducerBuildModules(projStep.id, rebuiltModules, projStep.dirtyJS)
|
||||
verifyJsCode(projStep.id, mainModuleName, jsOutput)
|
||||
verifyDTS(projStep.id, testInfo, jsOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -330,6 +330,11 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
|
||||
runTest("js/js.translator/testData/incremental/invalidation/transitiveInlineFunction/");
|
||||
}
|
||||
|
||||
@TestMetadata("typeScriptExports")
|
||||
public void testTypeScriptExports() throws Exception {
|
||||
runTest("js/js.translator/testData/incremental/invalidation/typeScriptExports/");
|
||||
}
|
||||
|
||||
@TestMetadata("unicodeSerializationAndDeserialization")
|
||||
public void testUnicodeSerializationAndDeserialization() throws Exception {
|
||||
runTest("js/js.translator/testData/incremental/invalidation/unicodeSerializationAndDeserialization/");
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
fun foo() = 0
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
@JsExport
|
||||
fun foo() = 0
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
@JsExport
|
||||
@JsName("bar")
|
||||
fun foo() = 0
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
STEP 0:
|
||||
modifications:
|
||||
U : l1.0.4.kt -> l1.kt
|
||||
added file: l1.kt
|
||||
STEP 1:
|
||||
modifications:
|
||||
U : l1.1.kt -> l1.kt
|
||||
modified ir: l1.kt
|
||||
STEP 2:
|
||||
modifications:
|
||||
U : l1.2.kt -> l1.kt
|
||||
modified ir: l1.kt
|
||||
STEP 3:
|
||||
STEP 4:
|
||||
modifications:
|
||||
U : l1.0.4.kt -> l1.kt
|
||||
modified ir: l1.kt
|
||||
STEP 5..6:
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class MyClass(val stepId: Int) {
|
||||
fun qux() = foo() + stepId
|
||||
}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
@JsExport
|
||||
class MyClass(val stepId: Int) {
|
||||
fun qux() = foo() + stepId
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
@JsExport
|
||||
class MyClass(val stepId: Int) {
|
||||
@JsName("baz")
|
||||
fun qux() = foo() + stepId
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
@JsExport
|
||||
class MyClass(val stepId: Int) {
|
||||
@JsName("bar")
|
||||
fun qux() = foo() + stepId
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
STEP 0:
|
||||
dependencies: lib1
|
||||
modifications:
|
||||
U : l2.0.kt -> l2.kt
|
||||
added file: l2.kt
|
||||
STEP 1..2:
|
||||
dependencies: lib1
|
||||
updated imports: l2.kt
|
||||
STEP 3:
|
||||
dependencies: lib1
|
||||
modifications:
|
||||
U : l2.3.kt -> l2.kt
|
||||
modified ir: l2.kt
|
||||
STEP 4:
|
||||
dependencies: lib1
|
||||
updated imports: l2.kt
|
||||
STEP 5:
|
||||
dependencies: lib1
|
||||
modifications:
|
||||
U : l2.5.kt -> l2.kt
|
||||
modified ir: l2.kt
|
||||
STEP 6:
|
||||
dependencies: lib1
|
||||
modifications:
|
||||
U : l2.6.kt -> l2.kt
|
||||
modified ir: l2.kt
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
function foo(): number;
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
function bar(): number;
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
function bar(): number;
|
||||
class MyClass {
|
||||
constructor(stepId: number);
|
||||
get stepId(): number;
|
||||
qux(): number;
|
||||
}
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
class MyClass {
|
||||
constructor(stepId: number);
|
||||
get stepId(): number;
|
||||
qux(): number;
|
||||
}
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
class MyClass {
|
||||
constructor(stepId: number);
|
||||
get stepId(): number;
|
||||
baz(): number;
|
||||
}
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
declare namespace kotlin_main {
|
||||
type Nullable<T> = T | null | undefined
|
||||
class MyClass {
|
||||
constructor(stepId: number);
|
||||
get stepId(): number;
|
||||
bar(): number;
|
||||
}
|
||||
function box(stepId: number): string;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun box(stepId: Int): String {
|
||||
val x = MyClass(stepId).qux()
|
||||
if (x != stepId) {
|
||||
return "Fail: $x != $stepId"
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
STEP 0:
|
||||
dependencies: lib1, lib2
|
||||
added file: m.kt
|
||||
expected dts: m.0.d.ts
|
||||
STEP 1:
|
||||
dependencies: lib1, lib2
|
||||
expected dts: m.1.d.ts
|
||||
STEP 2:
|
||||
dependencies: lib1, lib2
|
||||
expected dts: m.2.d.ts
|
||||
STEP 3:
|
||||
dependencies: lib1, lib2
|
||||
updated imports: m.kt
|
||||
expected dts: m.3.d.ts
|
||||
STEP 4:
|
||||
dependencies: lib1, lib2
|
||||
expected dts: m.4.d.ts
|
||||
STEP 5:
|
||||
dependencies: lib1, lib2
|
||||
updated imports: m.kt
|
||||
expected dts: m.5.d.ts
|
||||
STEP 6:
|
||||
dependencies: lib1, lib2
|
||||
updated imports: m.kt
|
||||
expected dts: m.6.d.ts
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
MODULES: lib1, lib2, main
|
||||
|
||||
STEP 0..1:
|
||||
libs: lib1, lib2, main
|
||||
dirty js: lib1, lib2, main
|
||||
STEP 2:
|
||||
libs: lib1, lib2, main
|
||||
dirty js: lib1, lib2
|
||||
STEP 3:
|
||||
libs: lib1, lib2, main
|
||||
dirty js: lib2, main
|
||||
STEP 4:
|
||||
libs: lib1, lib2, main
|
||||
dirty js: lib1, lib2, main
|
||||
STEP 5..6:
|
||||
libs: lib1, lib2, main
|
||||
dirty js: lib2, main
|
||||
Reference in New Issue
Block a user