[K/JS] Rework ES modules part with squashed JsImport and right renaming strategy inside import/export statements
This commit is contained in:
@@ -1342,7 +1342,7 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
blockOpen();
|
||||
List<JsExport.Element> elements = ((JsExport.Subject.Elements) subject).getElements();
|
||||
for (JsExport.Element element : elements) {
|
||||
nameDef(element.getName());
|
||||
visitNameRef(element.getName());
|
||||
JsName alias = element.getAlias();
|
||||
if (alias != null) {
|
||||
p.print(" as ");
|
||||
@@ -1369,10 +1369,10 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
p.print("import ");
|
||||
|
||||
if (target instanceof JsImport.Target.Default) {
|
||||
nameDef(((JsImport.Target.Default) target).getName());
|
||||
visitNameRef(((JsImport.Target.Default) target).getName());
|
||||
} else if (target instanceof JsImport.Target.All) {
|
||||
p.print("* as ");
|
||||
nameDef(((JsImport.Target.All) target).getAlias());
|
||||
visitNameRef(((JsImport.Target.All) target).getAlias());
|
||||
} else if (target instanceof JsImport.Target.Elements) {
|
||||
List<JsImport.Element> elements = ((JsImport.Target.Elements) target).getElements();
|
||||
|
||||
@@ -1386,10 +1386,10 @@ public class JsToStringGenerationVisitor extends JsVisitor {
|
||||
|
||||
for (JsImport.Element element : elements) {
|
||||
nameDef(element.getName());
|
||||
JsName alias = element.getAlias();
|
||||
JsNameRef alias = element.getAlias();
|
||||
if (alias != null) {
|
||||
p.print(" as ");
|
||||
nameDef(alias);
|
||||
visitNameRef(alias);
|
||||
}
|
||||
|
||||
if (isMultiline) {
|
||||
|
||||
@@ -11,7 +11,7 @@ class JsExport(
|
||||
) : SourceInfoAwareJsNode(), JsStatement {
|
||||
|
||||
constructor(element: Element) : this(Subject.Elements(listOf(element)))
|
||||
constructor(name: JsName, alias: JsName? = null) : this(Element(name, alias))
|
||||
constructor(name: JsNameRef, alias: JsName? = null) : this(Element(name, alias))
|
||||
|
||||
sealed class Subject {
|
||||
class Elements(val elements: List<Element>) : Subject()
|
||||
@@ -19,7 +19,7 @@ class JsExport(
|
||||
}
|
||||
|
||||
class Element(
|
||||
val name: JsName,
|
||||
val name: JsNameRef,
|
||||
val alias: JsName?
|
||||
)
|
||||
|
||||
@@ -28,6 +28,11 @@ class JsExport(
|
||||
}
|
||||
|
||||
override fun acceptChildren(visitor: JsVisitor) {
|
||||
if (subject is Subject.Elements) {
|
||||
subject.elements.forEach {
|
||||
visitor.accept(it.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deepCopy(): JsStatement =
|
||||
|
||||
@@ -9,34 +9,39 @@ class JsImport(
|
||||
val module: String,
|
||||
val target: Target,
|
||||
) : SourceInfoAwareJsNode(), JsStatement {
|
||||
constructor(module: String, elements: MutableList<Element> = mutableListOf()) : this(module, Target.Elements(elements))
|
||||
constructor(module: String, vararg elements: Element) : this(module, Target.Elements(elements.toMutableList()))
|
||||
|
||||
val elements: MutableList<Element>
|
||||
get() = (target as Target.Elements).elements
|
||||
|
||||
sealed class Target {
|
||||
class Elements(val elements: MutableList<Element>) : Target()
|
||||
class Default(val name: JsName) : Target() {
|
||||
constructor(name: String) : this(JsName(name, false))
|
||||
class Default(val name: JsNameRef) : Target() {
|
||||
constructor(name: String) : this(JsNameRef(name))
|
||||
}
|
||||
|
||||
class All(val alias: JsName) : Target() {
|
||||
constructor(alias: String) : this(JsName(alias, false))
|
||||
class All(val alias: JsNameRef) : Target() {
|
||||
constructor(alias: String) : this(JsNameRef(alias))
|
||||
}
|
||||
}
|
||||
|
||||
class Element(
|
||||
val name: JsName,
|
||||
val alias: JsName?
|
||||
) {
|
||||
constructor(name: String, alias: String?) : this(JsName(name, false), alias?.let { JsName(it, false) })
|
||||
}
|
||||
val alias: JsNameRef?
|
||||
)
|
||||
|
||||
override fun accept(visitor: JsVisitor) {
|
||||
visitor.visitImport(this)
|
||||
}
|
||||
|
||||
override fun acceptChildren(visitor: JsVisitor) {
|
||||
when (target) {
|
||||
is Target.All -> visitor.accept(target.alias)
|
||||
is Target.Default -> visitor.accept(target.name)
|
||||
is Target.Elements -> target.elements.forEach {
|
||||
it.alias?.let(visitor::accept)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun deepCopy(): JsStatement =
|
||||
|
||||
@@ -26,7 +26,14 @@ class JsImportedModule @JvmOverloads constructor(
|
||||
val key = JsImportedModuleKey(externalName, plainReference?.toString())
|
||||
}
|
||||
|
||||
val JsImportedModule.requireName: String
|
||||
get() = relativeRequirePath?.let { "$it.js" } ?: externalName
|
||||
const val REGULAR_EXTENSION = ".js"
|
||||
const val ESM_EXTENSION = ".mjs"
|
||||
|
||||
fun JsImportedModule.getRequireName(isEsm: Boolean = false): String {
|
||||
return relativeRequirePath?.let {
|
||||
val extension = if (isEsm) ESM_EXTENSION else REGULAR_EXTENSION
|
||||
"$it$extension"
|
||||
} ?: externalName
|
||||
}
|
||||
|
||||
data class JsImportedModuleKey(val baseName: String, val plainName: String?)
|
||||
@@ -15,14 +15,14 @@ import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.backend.js.ic.JsExecutableProducer
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
|
||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||
import org.jetbrains.kotlin.ir.util.irMessageLogger
|
||||
import org.jetbrains.kotlin.js.backend.ast.ESM_EXTENSION
|
||||
import org.jetbrains.kotlin.js.backend.ast.REGULAR_EXTENSION
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION
|
||||
import org.jetbrains.kotlin.js.test.utils.extractTestPackage
|
||||
@@ -37,13 +37,12 @@ import org.jetbrains.kotlin.test.frontend.classic.moduleDescriptorProvider
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator.Companion.getJsArtifactSimpleName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
|
||||
import java.io.File
|
||||
|
||||
const val REGULAR_EXTENSION = ".js"
|
||||
const val ESM_EXTENSION = ".mjs"
|
||||
|
||||
class JsIrBackendFacade(
|
||||
val testServices: TestServices,
|
||||
private val firstTimeCompilation: Boolean
|
||||
@@ -132,10 +131,10 @@ class JsIrBackendFacade(
|
||||
|
||||
|
||||
val loweredIr = compileIr(
|
||||
irModuleFragment.apply { resolveTestPathes() },
|
||||
irModuleFragment.apply { resolveTestPaths() },
|
||||
MainModule.Klib(inputArtifact.outputFile.absolutePath),
|
||||
configuration,
|
||||
dependencyModules.apply { forEach { it.resolveTestPathes() } },
|
||||
dependencyModules.onEach { it.resolveTestPaths() },
|
||||
emptyMap(),
|
||||
irModuleFragment.irBuiltins,
|
||||
symbolTable,
|
||||
@@ -150,14 +149,10 @@ class JsIrBackendFacade(
|
||||
granularity = granularity,
|
||||
)
|
||||
|
||||
return loweredIr2JsArtifact(module, loweredIr, granularity)
|
||||
return loweredIr2JsArtifact(module, loweredIr)
|
||||
}
|
||||
|
||||
private fun loweredIr2JsArtifact(
|
||||
module: TestModule,
|
||||
loweredIr: LoweredIr,
|
||||
granularity: JsGenerationGranularity,
|
||||
): BinaryArtifacts.Js {
|
||||
private fun loweredIr2JsArtifact(module: TestModule, loweredIr: LoweredIr): BinaryArtifacts.Js {
|
||||
val mainArguments = JsEnvironmentConfigurator.getMainCallParametersForModule(module)
|
||||
.run { if (shouldBeGenerated()) arguments() else null }
|
||||
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
|
||||
@@ -172,10 +167,13 @@ class JsIrBackendFacade(
|
||||
val transformer = IrModuleToJsTransformer(
|
||||
loweredIr.context,
|
||||
mainArguments,
|
||||
moduleToName = JsIrModuleToPath(
|
||||
testServices,
|
||||
isEsModules && granularity != JsGenerationGranularity.WHOLE_PROGRAM
|
||||
)
|
||||
moduleToName = runIf(isEsModules) {
|
||||
loweredIr.allModules.associateWith {
|
||||
"./${getJsArtifactSimpleName(testServices, it.safeName)}_v5.mjs".run {
|
||||
if (isWindows) minify() else this
|
||||
}
|
||||
}
|
||||
} ?: emptyMap()
|
||||
)
|
||||
// If runIrDce then include DCE results
|
||||
// If perModuleOnly then skip whole program
|
||||
@@ -188,7 +186,7 @@ class JsIrBackendFacade(
|
||||
return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module)
|
||||
}
|
||||
|
||||
private fun IrModuleFragment.resolveTestPathes() {
|
||||
private fun IrModuleFragment.resolveTestPaths() {
|
||||
JsIrPathReplacer(testServices).let {
|
||||
files.forEach(it::lower)
|
||||
}
|
||||
@@ -274,7 +272,7 @@ class JsIrBackendFacade(
|
||||
|
||||
private fun CompilationOutputs.writeTo(outputFile: File, moduleId: String, moduleKind: ModuleKind) {
|
||||
val allJsFiles = writeAll(outputFile.parentFile, outputFile.nameWithoutExtension, false, moduleId, moduleKind).filter {
|
||||
it.extension == "js"
|
||||
it.extension == "js" || it.extension == "mjs"
|
||||
}
|
||||
|
||||
val mainModuleFile = allJsFiles.last()
|
||||
@@ -296,12 +294,6 @@ class JsIrBackendFacade(
|
||||
}
|
||||
}
|
||||
|
||||
val ModuleKind.extension: String
|
||||
get() = when (this) {
|
||||
ModuleKind.ES -> ESM_EXTENSION
|
||||
else -> REGULAR_EXTENSION
|
||||
}
|
||||
|
||||
val RegisteredDirectives.moduleKind: ModuleKind
|
||||
get() = get(JsEnvironmentConfigurationDirectives.MODULE_KIND).singleOrNull()
|
||||
?: if (contains(JsEnvironmentConfigurationDirectives.ES_MODULES)) ModuleKind.ES else ModuleKind.PLAIN
|
||||
@@ -311,15 +303,15 @@ val TestModule.kind: ModuleKind
|
||||
|
||||
fun String.augmentWithModuleName(moduleName: String): String {
|
||||
val normalizedName = moduleName.run { if (isWindows) minify() else this }
|
||||
val suffix = when {
|
||||
endsWith(ESM_EXTENSION) -> ESM_EXTENSION
|
||||
endsWith(REGULAR_EXTENSION) -> REGULAR_EXTENSION
|
||||
else -> error("Unexpected file '$this' extension")
|
||||
}
|
||||
|
||||
return if (normalizedName.isPath()) {
|
||||
replaceAfterLast(File.separator, normalizedName.replace("./", ""))
|
||||
return if (suffix == ESM_EXTENSION) {
|
||||
replaceAfterLast(File.separator, normalizedName.replace("./", "")).removeSuffix(suffix) + suffix
|
||||
} else {
|
||||
val suffix = when {
|
||||
endsWith(ESM_EXTENSION) -> ESM_EXTENSION
|
||||
endsWith(REGULAR_EXTENSION) -> REGULAR_EXTENSION
|
||||
else -> error("Unexpected file '$this' extension")
|
||||
}
|
||||
return removeSuffix("_v5$suffix") + "-${normalizedName}_v5$suffix"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.converters
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.isWindows
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator.Companion.getJsArtifactSimpleName
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
private typealias K = IrModuleFragment
|
||||
private typealias V = String
|
||||
|
||||
class JsIrModuleToPath(val testServices: TestServices, shouldProvidePaths: Boolean) : Map<K, V> {
|
||||
override val size = if (!shouldProvidePaths) 0 else 1
|
||||
override val entries = emptySet<Map.Entry<K, V>>()
|
||||
override val keys = emptySet<K>()
|
||||
override val values = emptyList<V>()
|
||||
|
||||
override fun isEmpty() = size == 0
|
||||
override fun containsKey(key: K): Boolean = !isEmpty()
|
||||
override fun containsValue(value: V): Boolean = !isEmpty()
|
||||
|
||||
override operator fun get(key: K): V? {
|
||||
return runIf(!isEmpty()) {
|
||||
"./${getJsArtifactSimpleName(testServices, key.safeName)}_v5.mjs".run {
|
||||
if (isWindows) minify() else this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@
|
||||
package org.jetbrains.kotlin.js.test.utils
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.extension
|
||||
import org.jetbrains.kotlin.js.JavaScript
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.test.JsAdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.js.test.converters.augmentWithModuleName
|
||||
import org.jetbrains.kotlin.js.test.converters.extension
|
||||
import org.jetbrains.kotlin.js.test.converters.kind
|
||||
import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION
|
||||
import org.jetbrains.kotlin.js.testOld.*
|
||||
@@ -131,7 +131,7 @@ fun testWithModuleSystem(testServices: TestServices): Boolean {
|
||||
val globalDirectives = testServices.moduleStructure.allDirectives
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(getMainModule(testServices))
|
||||
val mainModuleKind = configuration[JSConfigurationKeys.MODULE_KIND]
|
||||
return mainModuleKind != ModuleKind.PLAIN && NO_JS_MODULE_SYSTEM !in globalDirectives
|
||||
return mainModuleKind != ModuleKind.PLAIN && mainModuleKind != ModuleKind.ES && NO_JS_MODULE_SYSTEM !in globalDirectives
|
||||
}
|
||||
|
||||
fun getModeOutputFilePath(testServices: TestServices, module: TestModule, mode: TranslationMode): String {
|
||||
|
||||
@@ -8934,6 +8934,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObjectInSeparatedFile.kt")
|
||||
public void testFindAssociatedObjectInSeparatedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObjectInSeparatedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObject_oldBE.kt")
|
||||
public void testFindAssociatedObject_oldBE() throws Exception {
|
||||
|
||||
+6
-6
@@ -2297,12 +2297,6 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reexport.kt")
|
||||
public void testReexport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reexport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
@@ -9844,6 +9838,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObjectInSeparatedFile.kt")
|
||||
public void testFindAssociatedObjectInSeparatedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObjectInSeparatedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObject_oldBE.kt")
|
||||
public void testFindAssociatedObject_oldBE() throws Exception {
|
||||
|
||||
+6
-6
@@ -2403,12 +2403,6 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reexport.kt")
|
||||
public void testReexport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reexport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
@@ -9950,6 +9944,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObjectInSeparatedFile.kt")
|
||||
public void testFindAssociatedObjectInSeparatedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObjectInSeparatedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObject_oldBE.kt")
|
||||
public void testFindAssociatedObject_oldBE() throws Exception {
|
||||
|
||||
+6
-6
@@ -2297,12 +2297,6 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/esModules/export/overriddenExternalMethodWithSameStableNameMethodInExportedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reexport.kt")
|
||||
public void testReexport() throws Exception {
|
||||
runTest("js/js.translator/testData/box/esModules/export/reexport.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("reservedModuleName.kt")
|
||||
public void testReservedModuleName() throws Exception {
|
||||
@@ -9844,6 +9838,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObject.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObjectInSeparatedFile.kt")
|
||||
public void testFindAssociatedObjectInSeparatedFile() throws Exception {
|
||||
runTest("js/js.translator/testData/box/reflection/findAssociatedObjectInSeparatedFile.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("findAssociatedObject_oldBE.kt")
|
||||
public void testFindAssociatedObject_oldBE() throws Exception {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// IGNORE_FIR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS
|
||||
// ES_MODULES
|
||||
// EXPECTED_REACHABLE_NODES: 1283
|
||||
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
@JsExport
|
||||
fun bar() = "O"
|
||||
|
||||
// MODULE: lib2(lib1)
|
||||
// FILE: lib2.kt
|
||||
|
||||
@JsExport
|
||||
fun baz() = "K"
|
||||
|
||||
// MODULE: main(lib2)
|
||||
// FILE: main.kt
|
||||
|
||||
@JsExport
|
||||
fun result(o: String, k: String) = o + k
|
||||
|
||||
// FILE: entry.mjs
|
||||
// ENTRY_ES_MODULE
|
||||
import { bar, baz, result } from "./reexport_v5.mjs";
|
||||
|
||||
export function box() {
|
||||
const o = bar();
|
||||
const k = baz();
|
||||
return result(o, k);
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
|
||||
// FILE: annotations.kt
|
||||
import kotlin.reflect.*
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
@AssociatedObjectKey
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Associated3(val kClass: KClass<*>)
|
||||
|
||||
// FILE: foo.kt
|
||||
@Associated1(Bar::class)
|
||||
@Associated2(Baz::class)
|
||||
class Foo
|
||||
|
||||
// FILE: bar.kt
|
||||
import kotlin.reflect.*
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
@AssociatedObjectKey
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Associated1(val kClass: KClass<*>)
|
||||
|
||||
object Bar
|
||||
|
||||
// FILE: baz.kt
|
||||
import kotlin.reflect.*
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
@AssociatedObjectKey
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Associated2(val kClass: KClass<*>)
|
||||
|
||||
object Baz
|
||||
|
||||
private class C(var list: List<String>?)
|
||||
|
||||
private interface I1 {
|
||||
fun foo(): Int
|
||||
fun bar(c: C)
|
||||
}
|
||||
|
||||
private object I1Impl : I1 {
|
||||
override fun foo() = 42
|
||||
override fun bar(c: C) {
|
||||
c.list = mutableListOf("zzz")
|
||||
}
|
||||
}
|
||||
|
||||
@Associated1(I1Impl::class)
|
||||
private class I1ImplHolder
|
||||
|
||||
private interface I2 {
|
||||
fun foo(): Int
|
||||
}
|
||||
|
||||
private object I2Impl : I2 {
|
||||
override fun foo() = 17
|
||||
}
|
||||
|
||||
@Associated1(I2Impl::class)
|
||||
private class I2ImplHolder
|
||||
|
||||
@Associated2(A.Companion::class)
|
||||
class A {
|
||||
companion object : I2 {
|
||||
override fun foo() = 20
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
fun KClass<*>.getAssociatedObjectByAssociated2(): Any? {
|
||||
return this.findAssociatedObject<Associated2>()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAssociatedObjects::class)
|
||||
fun box(): String {
|
||||
|
||||
if (Foo::class.findAssociatedObject<Associated1>() != Bar) return "fail 1"
|
||||
|
||||
if (Foo::class.findAssociatedObject<Associated2>() != Baz) return "fail 2"
|
||||
|
||||
if (Foo::class.findAssociatedObject<Associated3>() != null) return "fail 3"
|
||||
|
||||
if (Bar::class.findAssociatedObject<Associated1>() != null) return "fail 4"
|
||||
|
||||
val i1 = I1ImplHolder::class.findAssociatedObject<Associated1>() as I1
|
||||
if (i1.foo() != 42) return "fail 5"
|
||||
|
||||
val c = C(null)
|
||||
i1.bar(c)
|
||||
if (c.list!![0] != "zzz") return "fail 6"
|
||||
|
||||
val i2 = I2ImplHolder()::class.findAssociatedObject<Associated1>() as I2
|
||||
if (i2.foo() != 17) return "fail 7"
|
||||
|
||||
val a = A::class.findAssociatedObject<Associated2>() as I2
|
||||
if (a.foo() != 20) return "fail 8"
|
||||
|
||||
if (Foo::class.getAssociatedObjectByAssociated2() != Baz) return "fail 9"
|
||||
|
||||
if ((A::class.getAssociatedObjectByAssociated2() as I2).foo() != 20) return "fail 10"
|
||||
|
||||
if (Int::class.findAssociatedObject<Associated1>() != null) return "fail 11"
|
||||
|
||||
if (10::class.findAssociatedObject<Associated2>() != null) return "fail 12"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
Reference in New Issue
Block a user