[CHERRY PICKED FROM IJ] [Import] Introduce new version of KotlinFacetSettings, improve serialization of compiler arguments

^KTIJ-13451 Fixed

GitOrigin-RevId: 17ff1ea6c947b7582b445004959c42e54819d31a
Original commit: https://github.com/JetBrains/intellij-community/commit/14bd2509ad37f0c818f6753b097d4c138fba5d89
This commit is contained in:
Yaroslav Chernyshev
2021-07-14 13:53:00 +03:00
committed by Nikita Bobko
parent 1c634ee6e2
commit f6d9e53b66
8 changed files with 756 additions and 21 deletions
@@ -0,0 +1,46 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
import org.jetbrains.kotlin.cli.common.arguments.Argument
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.InternalArgument
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.KType
import kotlin.reflect.full.memberProperties
object CompilerArgumentsContentProspector {
private val argumentPropertiesCache: MutableMap<KClass<out CommonToolArguments>, List<KProperty1<out CommonToolArguments, *>>> =
mutableMapOf()
private val flagArgumentPropertiesCache: MutableMap<KClass<out CommonToolArguments>, List<KProperty1<out CommonToolArguments, Boolean>>> =
mutableMapOf()
private val stringArgumentPropertiesCache: MutableMap<KClass<out CommonToolArguments>, List<KProperty1<out CommonToolArguments, String?>>> =
mutableMapOf()
private val arrayArgumentPropertiesCache: MutableMap<KClass<out CommonToolArguments>, List<KProperty1<out CommonToolArguments, Array<String>?>>> =
mutableMapOf()
private fun getCompilerArguments(kClass: KClass<out CommonToolArguments>) = argumentPropertiesCache.getOrPut(kClass) {
kClass.memberProperties.filter { prop -> prop.annotations.singleOrNull { it is Argument } != null }
}
private inline fun <reified R : Any> List<KProperty1<out CommonToolArguments, *>>.filterByReturnType(predicate: (KType?) -> Boolean) =
filter { predicate(it.returnType) }.mapNotNull { it.safeAs<KProperty1<CommonToolArguments, R>>() }
fun getFlagCompilerArgumentProperties(kClass: KClass<out CommonToolArguments>): List<KProperty1<out CommonToolArguments, Boolean>> =
flagArgumentPropertiesCache.getOrPut(kClass) { getCompilerArguments(kClass).filterByReturnType { it?.classifier == Boolean::class } }
fun <T : CommonToolArguments> getStringCompilerArgumentProperties(kClass: KClass<T>): List<KProperty1<out CommonToolArguments, String?>> =
stringArgumentPropertiesCache.getOrPut(kClass) { getCompilerArguments(kClass).filterByReturnType { it?.classifier == String::class } }
fun <T : CommonToolArguments> getArrayCompilerArgumentProperties(kClass: KClass<T>): List<KProperty1<out CommonToolArguments, Array<String>?>> =
arrayArgumentPropertiesCache.getOrPut(kClass) { getCompilerArguments(kClass).filterByReturnType { it?.classifier?.javaClass?.isArray == true } }
val freeArgsProperty: KProperty1<in CommonToolArguments, List<String>>
get() = CommonToolArguments::freeArgs
val internalArgumentsProperty: KProperty1<in CommonToolArguments, List<InternalArgument>>
get() = CommonToolArguments::internalArguments
}
@@ -0,0 +1,109 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
import org.jdom.Element
import org.jdom.Text
import org.jetbrains.kotlin.cli.common.arguments.ArgumentParseErrors
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.cli.common.arguments.InternalArgument
import org.jetbrains.kotlin.cli.common.arguments.LanguageSettingsParser
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
import kotlin.reflect.KMutableProperty1
interface CompilerArgumentsDeserializer<T : CommonToolArguments> {
val compilerArguments: T
fun deserializeFrom(element: Element)
}
class CompilerArgumentsDeserializerV5<T : CommonToolArguments>(override val compilerArguments: T) : CompilerArgumentsDeserializer<T> {
override fun deserializeFrom(element: Element) {
val flagArgumentsByName = readFlagArguments(element)
val flagArgumentsPropertiesMap = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(compilerArguments::class)
.associateBy { it.name }
flagArgumentsByName.forEach { (name, value) ->
val mutableProp = flagArgumentsPropertiesMap[name].safeAs<KMutableProperty1<T, Boolean>>() ?: return@forEach
mutableProp.set(compilerArguments, value)
}
val stringArgumentsByName = readStringArguments(element)
val stringArgumentPropertiesMap = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(compilerArguments::class)
.associateBy { it.name }
stringArgumentsByName.forEach { (name, arg) ->
val mutableProp = stringArgumentPropertiesMap[name].safeAs<KMutableProperty1<T, String?>>() ?: return@forEach
mutableProp.set(compilerArguments, arg)
}
val arrayArgumentsByName = readArrayArguments(element)
val arrayArgumentPropertiesMap = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(compilerArguments::class)
.associateBy { it.name }
arrayArgumentsByName.forEach { (name, arr) ->
val mutableProp = arrayArgumentPropertiesMap[name].safeAs<KMutableProperty1<T, Array<String>?>>() ?: return@forEach
mutableProp.set(compilerArguments, arr)
}
val freeArgs = readElementsList(element, FREE_ARGS_ROOT_ELEMENTS_NAME, FREE_ARGS_ELEMENT_NAME)
CompilerArgumentsContentProspector.freeArgsProperty.also {
val mutableProp = it.safeAs<KMutableProperty1<T, List<String>>>() ?: return@also
mutableProp.set(compilerArguments, freeArgs)
}
val internalArguments = readElementsList(element, INTERNAL_ARGS_ROOT_ELEMENTS_NAME, INTERNAL_ARGS_ELEMENT_NAME)
.mapNotNull { parseInternalArgument(it) }
CompilerArgumentsContentProspector.internalArgumentsProperty.safeAs<KMutableProperty1<T, List<InternalArgument>>>()
?.set(compilerArguments, internalArguments)
}
companion object {
private fun readElementConfigurable(element: Element, rootElementName: String, configurable: Element.() -> Unit) {
element.getChild(rootElementName)?.apply { configurable(this) }
}
private fun readStringArguments(element: Element): Map<String, String> = mutableMapOf<String, String>().also {
readElementConfigurable(element, STRING_ROOT_ELEMENTS_NAME) {
getChildren(STRING_ELEMENT_NAME).forEach { child ->
val name = child.getAttribute(NAME_ATTR_NAME)?.value ?: return@forEach
val arg = if (name == "classpath")
readElementsList(child, ARGS_ATTR_NAME, ARG_ATTR_NAME).joinToString(File.pathSeparator)
else child.getAttribute(ARG_ATTR_NAME)?.value ?: return@forEach
it += name to arg
}
}
}
private fun readFlagArguments(element: Element): Map<String, Boolean> = mutableMapOf<String, Boolean>().also {
readElementConfigurable(element, FLAG_ROOT_ELEMENTS_NAME) {
getChildren(FLAG_ELEMENT_NAME).forEach { child ->
val name = child.getAttribute(NAME_ATTR_NAME)?.value ?: return@forEach
val arg = child.getAttribute(ARG_ATTR_NAME)?.booleanValue ?: return@forEach
it += name to arg
}
}
}
private fun readElementsList(element: Element, rootElementName: String, elementName: String): List<String> =
mutableListOf<String>().also { list ->
readElementConfigurable(element, rootElementName) {
val items = getChildren(elementName)
if (items.isNotEmpty()) {
items.mapNotNullTo(list) { (it.content.firstOrNull() as? Text)?.textTrim }
} else {
list += listOfNotNull((content.firstOrNull() as? Text)?.textTrim)
}
}
}
private fun readArrayArguments(element: Element): Map<String, Array<String>> = mutableMapOf<String, Array<String>>().apply {
readElementConfigurable(element, ARRAY_ROOT_ELEMENTS_NAME) {
getChildren(ARRAY_ELEMENT_NAME).forEach { child ->
val name = child.getAttribute(NAME_ATTR_NAME)?.value ?: return@forEach
val array = readElementsList(child, ARGS_ATTR_NAME, ARG_ATTR_NAME).toTypedArray()
this@apply += name to array
}
}
}
private fun parseInternalArgument(argument: String): InternalArgument? {
val parser = LanguageSettingsParser()
return parser.parseInternalArgument(argument, ArgumentParseErrors())
}
}
}
@@ -0,0 +1,115 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
import org.jdom.Element
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
import org.jetbrains.kotlin.config.restoreNormalOrdering
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.io.File
import kotlin.reflect.KProperty1
interface CompilerArgumentsSerializer<T : CommonToolArguments> {
val arguments: T
fun serializeTo(element: Element): Element
}
class CompilerArgumentsSerializerV5<T : CommonToolArguments>(override val arguments: T) : CompilerArgumentsSerializer<T> {
override fun serializeTo(element: Element): Element = Element(COMPILER_ARGUMENTS_ELEMENT_NAME).apply {
val newInstance = arguments::class.java.getConstructor().newInstance()
val flagArgumentsByName = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(arguments::class)
.mapNotNull { prop ->
prop.safeAs<KProperty1<T, Boolean>>()
?.takeIf { it.get(arguments) != it.get(newInstance) }
?.get(arguments)
?.let { prop.name to it }
}.toMap()
saveFlagArguments(this, flagArgumentsByName)
val stringArgumentsByName = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(arguments::class)
.mapNotNull { prop ->
prop.safeAs<KProperty1<T, String?>>()
?.takeIf { it.get(arguments) != it.get(newInstance) }
?.get(arguments)
?.let { prop.name to it }
}.toMap()
saveStringArguments(this, stringArgumentsByName)
val arrayArgumentsByName = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(arguments::class)
.mapNotNull { prop ->
prop.safeAs<KProperty1<T, Array<String>?>>()
?.takeIf { it.get(arguments)?.contentEquals(it.get(newInstance)) != true }
?.get(arguments)
?.let { prop.name to it }
}.filterNot { it.second.isEmpty() }
.toMap()
saveArrayArguments(this, arrayArgumentsByName)
val freeArgs = CompilerArgumentsContentProspector.freeArgsProperty.get(arguments)
saveElementsList(this, FREE_ARGS_ROOT_ELEMENTS_NAME, FREE_ARGS_ELEMENT_NAME, freeArgs)
val internalArguments = CompilerArgumentsContentProspector.internalArgumentsProperty.get(arguments).map { it.stringRepresentation }
saveElementsList(this, INTERNAL_ARGS_ROOT_ELEMENTS_NAME, INTERNAL_ARGS_ELEMENT_NAME, internalArguments)
restoreNormalOrdering(arguments)
element.addContent(this)
}
companion object {
private fun saveElementConfigurable(element: Element, rootElementName: String, configurable: Element.() -> Unit) {
element.addContent(Element(rootElementName).apply { configurable(this) })
}
private fun saveStringArguments(element: Element, argumentsByName: Map<String, String>) {
if (argumentsByName.isEmpty()) return
saveElementConfigurable(element, STRING_ROOT_ELEMENTS_NAME) {
argumentsByName.entries.forEach { (name, arg) ->
Element(STRING_ELEMENT_NAME).also {
it.setAttribute(NAME_ATTR_NAME, name)
if (name == "classpath") {
saveElementsList(it, ARGS_ATTR_NAME, ARG_ATTR_NAME, arg.split(File.pathSeparator))
} else {
it.setAttribute(ARG_ATTR_NAME, arg)
}
addContent(it)
}
}
}
}
private fun saveFlagArguments(element: Element, argumentsByName: Map<String, Boolean>) {
if (argumentsByName.isEmpty()) return
saveElementConfigurable(element, FLAG_ROOT_ELEMENTS_NAME) {
argumentsByName.entries.forEach { (name, arg) ->
Element(FLAG_ELEMENT_NAME).also {
it.setAttribute(NAME_ATTR_NAME, name)
it.setAttribute(ARG_ATTR_NAME, arg.toString())
addContent(it)
}
}
}
}
private fun saveElementsList(element: Element, rootElementName: String, elementName: String, elementList: List<String>) {
if (elementList.isEmpty()) return
saveElementConfigurable(element, rootElementName) {
val singleModule = elementList.singleOrNull()
if (singleModule != null) {
addContent(singleModule)
} else {
elementList.forEach { elementValue -> addContent(Element(elementName).also { it.addContent(elementValue) }) }
}
}
}
private fun saveArrayArguments(element: Element, arrayArgumentsByName: Map<String, Array<String>>) {
if (arrayArgumentsByName.isEmpty()) return
saveElementConfigurable(element, ARRAY_ROOT_ELEMENTS_NAME) {
arrayArgumentsByName.entries.forEach { (name, arg) ->
Element(ARRAY_ELEMENT_NAME).also {
it.setAttribute(NAME_ATTR_NAME, name)
saveElementsList(it, ARGS_ATTR_NAME, ARG_ATTR_NAME, arg.toList())
addContent(it)
}
}
}
}
}
}
@@ -0,0 +1,18 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
internal const val COMPILER_ARGUMENTS_ELEMENT_NAME = "compilerArguments"
internal const val FLAG_ROOT_ELEMENTS_NAME = "flagArguments"
internal const val FLAG_ELEMENT_NAME = "flagArg"
internal const val STRING_ROOT_ELEMENTS_NAME = "stringArguments"
internal const val STRING_ELEMENT_NAME = "stringArg"
internal const val ARRAY_ROOT_ELEMENTS_NAME = "arrayArguments"
internal const val ARRAY_ELEMENT_NAME = "arrayArg"
internal const val FREE_ARGS_ROOT_ELEMENTS_NAME = "freeArgs"
internal const val FREE_ARGS_ELEMENT_NAME = "freeArg"
internal const val INTERNAL_ARGS_ROOT_ELEMENTS_NAME = "internalArgs"
internal const val INTERNAL_ARGS_ELEMENT_NAME = "internalArg"
internal const val NAME_ATTR_NAME = "name"
internal const val ARG_ATTR_NAME = "arg"
internal const val ARGS_ATTR_NAME = "args"
@@ -159,7 +159,7 @@ data class ExternalSystemNativeMainRunTask(
class KotlinFacetSettings {
companion object {
// Increment this when making serialization-incompatible changes to configuration data
val CURRENT_VERSION = 4
val CURRENT_VERSION = 5
val DEFAULT_VERSION = 0
}
@@ -12,6 +12,9 @@ import com.intellij.util.xmlb.XmlSerializer
import org.jdom.DataConversionException
import org.jdom.Element
import org.jdom.Text
import org.jetbrains.kotlin.arguments.COMPILER_ARGUMENTS_ELEMENT_NAME
import org.jetbrains.kotlin.arguments.CompilerArgumentsDeserializerV5
import org.jetbrains.kotlin.arguments.CompilerArgumentsSerializerV5
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.platform.*
@@ -25,7 +28,7 @@ import java.lang.reflect.Modifier
import kotlin.reflect.KClass
import kotlin.reflect.full.superclasses
fun Element.getOption(name: String) = getChildren("option").firstOrNull { it.getAttribute("name").value == name }
fun Element.getOption(name: String) = getChildren("option").firstOrNull { name == it?.getAttribute("name")?.value }
private fun Element.getOptionValue(name: String) = getOption(name)?.getAttribute("value")?.value
@@ -124,7 +127,10 @@ fun Element.getFacetPlatformByConfigurationElement(): TargetPlatform {
}.orDefault() // finally, fallback to the default platform
}
private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
private fun readV2AndLaterConfig(
element: Element,
argumentReader: (Element, CommonToolArguments) -> Unit = { el, arg -> XmlSerializer.deserializeInto(arg, el) }
): KotlinFacetSettings {
return KotlinFacetSettings().apply {
element.getAttributeValue("useProjectSettings")?.let { useProjectSettings = it.toBoolean() }
val targetPlatform = element.getFacetPlatformByConfigurationElement()
@@ -164,12 +170,12 @@ private fun readV2AndLaterConfig(element: Element): KotlinFacetSettings {
compilerSettings = CompilerSettings()
XmlSerializer.deserializeInto(compilerSettings!!, it)
}
element.getChild("compilerArguments")?.let {
element.getChild(COMPILER_ARGUMENTS_ELEMENT_NAME)?.let {
compilerArguments = targetPlatform.createArguments {
freeArgs = mutableListOf()
internalArguments = mutableListOf()
}
XmlSerializer.deserializeInto(compilerArguments!!, it)
argumentReader(it, compilerArguments!!)
compilerArguments!!.detectVersionAutoAdvance()
}
productionOutputPath = element.getChild("productionOutputPath")?.let {
@@ -193,16 +199,12 @@ private fun readElementsList(element: Element, rootElementName: String, elementN
return null
}
private fun readV3Config(element: Element): KotlinFacetSettings {
return readV2AndLaterConfig(element)
}
private fun readV2Config(element: Element): KotlinFacetSettings {
return readV2AndLaterConfig(element)
}
private fun readLatestConfig(element: Element): KotlinFacetSettings {
return readV2AndLaterConfig(element)
return readV2AndLaterConfig(element) { el, bean -> CompilerArgumentsDeserializerV5(bean).deserializeFrom(el) }
}
fun deserializeFacetSettings(element: Element): KotlinFacetSettings {
@@ -213,8 +215,7 @@ fun deserializeFacetSettings(element: Element): KotlinFacetSettings {
} ?: KotlinFacetSettings.DEFAULT_VERSION
return when (version) {
1 -> readV1Config(element)
2 -> readV2Config(element)
3 -> readV3Config(element)
2, 3, 4 -> readV2Config(element)
KotlinFacetSettings.CURRENT_VERSION -> readLatestConfig(element)
else -> return KotlinFacetSettings() // Reset facet configuration if versions don't match
}.apply { this.version = version }
@@ -278,7 +279,7 @@ private val Class<*>.normalOrdering
// Replacing fields with delegated properties leads to unexpected reordering of entries in facet configuration XML
// It happens due to XmlSerializer using different orderings for field- and method-based accessors
// This code restores the original ordering
private fun Element.restoreNormalOrdering(bean: Any) {
internal fun Element.restoreNormalOrdering(bean: Any) {
val normalOrdering = bean.javaClass.normalOrdering
val elementsToReorder = this.getContent<Element> { it is Element && it.getAttribute("name")?.value in normalOrdering }
elementsToReorder.sortedBy { normalOrdering[it.getAttribute("name")?.value!!] }
@@ -293,7 +294,7 @@ private fun buildChildElement(element: Element, tag: String, bean: Any, filter:
}
}
private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
private fun KotlinFacetSettings.writeConfig(element: Element) {
val filter = SkipDefaultsSerializationFilter()
// TODO: Introduce new version of facet serialization. See https://youtrack.jetbrains.com/issue/KT-38235
@@ -362,9 +363,20 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
it.convertPathsToSystemIndependent()
buildChildElement(element, "compilerSettings", it, filter)
}
}
private fun KotlinFacetSettings.writeV2toV4Config(element: Element) = writeConfig(element).apply {
compilerArguments?.let { copyBean(it) }?.let {
it.convertPathsToSystemIndependent()
val compilerArgumentsXml = buildChildElement(element, "compilerArguments", it, filter)
val compilerArgumentsXml = buildChildElement(element, "compilerArguments", it, SkipDefaultsSerializationFilter())
compilerArgumentsXml.dropVersionsIfNecessary(it)
}
}
private fun KotlinFacetSettings.writeLatestConfig(element: Element) = writeConfig(element).apply {
compilerArguments?.let { copyBean(it) }?.let {
it.convertPathsToSystemIndependent()
val compilerArgumentsXml = CompilerArgumentsSerializerV5(it).serializeTo(element)
compilerArgumentsXml.dropVersionsIfNecessary(it)
}
}
@@ -400,15 +412,18 @@ fun Element.dropVersionsIfNecessary(settings: CommonCompilerArguments) {
}
}
fun KotlinFacetSettings.serializeFacetSettings(element: Element) {
val versionToWrite = when (version) {
2, 3 -> version
else -> KotlinFacetSettings.CURRENT_VERSION
fun KotlinFacetSettings.serializeFacetSettings(element: Element) = when (version) {
2, 3, 4 -> {
element.setAttribute("version", version.toString())
writeV2toV4Config(element)
}
else -> {
element.setAttribute("version", KotlinFacetSettings.CURRENT_VERSION.toString())
writeLatestConfig(element)
}
element.setAttribute("version", versionToWrite.toString())
writeLatestConfig(element)
}
private fun TargetPlatform.serializeComponentPlatforms(): String {
val componentPlatforms = componentPlatforms
val componentPlatformNames = componentPlatforms.mapTo(ArrayList()) { it.serializeToString() }
@@ -0,0 +1,263 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
import org.jetbrains.kotlin.cli.common.arguments.*
import org.junit.Test
class CompilerArgumentsContentProspectorTest {
@Test
fun testJVMArgumentsContent() {
val flagProperties = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(K2JVMCompilerArguments::class)
val stringProperties = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(K2JVMCompilerArguments::class)
val arrayProperties = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(K2JVMCompilerArguments::class)
assertContentEquals(flagProperties, k2JVMCompilerArgumentsFlagProperties) { sortedBy { it.name } }
assertContentEquals(stringProperties, k2JVMCompilerArgumentsStringProperties) { sortedBy { it.name } }
assertContentEquals(arrayProperties, k2JVMCompilerArgumentsArrayArgumentProperties) { sortedBy { it.name } }
}
@Test
fun testMetadataArgumentsContent() {
val flagProperties = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(K2MetadataCompilerArguments::class)
val stringProperties = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(K2MetadataCompilerArguments::class)
val arrayProperties = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(K2MetadataCompilerArguments::class)
assertContentEquals(flagProperties, k2MetadataCompilerArgumentsFlagProperties) { sortedBy { it.name } }
assertContentEquals(stringProperties, k2MetadataCompilerArgumentsStringProperties) { sortedBy { it.name } }
assertContentEquals(arrayProperties, k2MetadataCompilerArgumentsArrayProperties) { sortedBy { it.name } }
}
@Test
fun testJsArgumentsContent() {
val flagProperties = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(K2JSCompilerArguments::class)
val stringProperties = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(K2JSCompilerArguments::class)
val arrayProperties = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(K2JSCompilerArguments::class)
assertContentEquals(flagProperties, k2JSCompilerArgumentsFlagProperties) { sortedBy { it.name } }
assertContentEquals(stringProperties, k2JSCompilerArgumentsStringProperties) { sortedBy { it.name } }
assert(arrayProperties.isEmpty()) { "Expected empty arrayProperties, but actual ${arrayProperties.joinToString { it.name }}" }
}
@Test
fun testJsDceArgumentsContent() {
val flagProperties = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(K2JSDceArguments::class)
val stringProperties = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(K2JSDceArguments::class)
val arrayProperties = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(K2JSDceArguments::class)
assertContentEquals(flagProperties, k2JSDceCompilerArgumentsFlagProperties) { sortedBy { it.name } }
assertContentEquals(stringProperties, k2JSDceCompilerArgumentsStringProperties) { sortedBy { it.name } }
assertContentEquals(arrayProperties, k2JSDceCompilerArgumentsArrayProperties) { sortedBy { it.name } }
}
companion object {
private val commonToolArgumentsFlagProperties = listOf(
CommonToolArguments::allWarningsAsErrors,
CommonToolArguments::extraHelp,
CommonToolArguments::help,
CommonToolArguments::suppressWarnings,
CommonToolArguments::verbose,
CommonToolArguments::version,
)
private val commonCompilerArgumentsFlagProperties = commonToolArgumentsFlagProperties + listOf(
CommonCompilerArguments::allowKotlinPackage,
CommonCompilerArguments::progressiveMode,
CommonCompilerArguments::script,
CommonCompilerArguments::noInline,
CommonCompilerArguments::skipMetadataVersionCheck,
CommonCompilerArguments::skipPrereleaseCheck,
CommonCompilerArguments::allowKotlinPackage,
CommonCompilerArguments::reportOutputFiles,
CommonCompilerArguments::multiPlatform,
CommonCompilerArguments::noCheckActual,
CommonCompilerArguments::newInference,
CommonCompilerArguments::inlineClasses,
CommonCompilerArguments::polymorphicSignature,
CommonCompilerArguments::legacySmartCastAfterTry,
CommonCompilerArguments::effectSystem,
CommonCompilerArguments::readDeserializedContracts,
CommonCompilerArguments::properIeee754Comparisons,
CommonCompilerArguments::reportPerf,
CommonCompilerArguments::allowResultReturnType,
CommonCompilerArguments::listPhases,
CommonCompilerArguments::profilePhases,
CommonCompilerArguments::checkPhaseConditions,
CommonCompilerArguments::checkStickyPhaseConditions,
CommonCompilerArguments::useFir,
CommonCompilerArguments::useFirExtendedCheckers,
CommonCompilerArguments::disableUltraLightClasses,
CommonCompilerArguments::useMixedNamedArguments,
CommonCompilerArguments::expectActualLinker,
CommonCompilerArguments::disableDefaultScriptingPlugin,
CommonCompilerArguments::inferenceCompatibility
)
private val commonCompilerArgumentsStringProperties = listOf(
CommonCompilerArguments::intellijPluginRoot,
CommonCompilerArguments::dumpPerf,
CommonCompilerArguments::metadataVersion,
CommonCompilerArguments::dumpDirectory,
CommonCompilerArguments::dumpOnlyFqName,
CommonCompilerArguments::explicitApi
)
private val commonCompilerArgumentsArrayArgumentProperties = listOf(
CommonCompilerArguments::pluginOptions,
CommonCompilerArguments::pluginClasspaths,
CommonCompilerArguments::experimental,
CommonCompilerArguments::useExperimental,
CommonCompilerArguments::optIn,
CommonCompilerArguments::commonSources,
CommonCompilerArguments::disablePhases,
CommonCompilerArguments::verbosePhases,
CommonCompilerArguments::phasesToDumpBefore,
CommonCompilerArguments::phasesToDumpAfter,
CommonCompilerArguments::phasesToDump,
CommonCompilerArguments::namesExcludedFromDumping,
CommonCompilerArguments::phasesToValidateBefore,
CommonCompilerArguments::phasesToValidateAfter,
CommonCompilerArguments::phasesToValidate,
)
private val k2JVMCompilerArgumentsFlagProperties = commonCompilerArgumentsFlagProperties + listOf(
K2JVMCompilerArguments::includeRuntime,
K2JVMCompilerArguments::noJdk,
K2JVMCompilerArguments::noStdlib,
K2JVMCompilerArguments::noReflect,
K2JVMCompilerArguments::javaParameters,
K2JVMCompilerArguments::useIR,
K2JVMCompilerArguments::useOldBackend,
K2JVMCompilerArguments::allowUnstableDependencies,
K2JVMCompilerArguments::doNotClearBindingContext,
K2JVMCompilerArguments::noCallAssertions,
K2JVMCompilerArguments::noReceiverAssertions,
K2JVMCompilerArguments::noParamAssertions,
K2JVMCompilerArguments::strictJavaNullabilityAssertions,
K2JVMCompilerArguments::noOptimize,
K2JVMCompilerArguments::inheritMultifileParts,
K2JVMCompilerArguments::useTypeTable,
K2JVMCompilerArguments::skipRuntimeVersionCheck,
K2JVMCompilerArguments::useOldClassFilesReading,
K2JVMCompilerArguments::singleModule,
K2JVMCompilerArguments::suppressMissingBuiltinsError,
K2JVMCompilerArguments::useJavac,
K2JVMCompilerArguments::compileJava,
K2JVMCompilerArguments::noExceptionOnExplicitEqualsForBoxedNull,
K2JVMCompilerArguments::disableStandardScript,
K2JVMCompilerArguments::strictMetadataVersionSemantics,
K2JVMCompilerArguments::sanitizeParentheses,
K2JVMCompilerArguments::allowNoSourceFiles,
K2JVMCompilerArguments::emitJvmTypeAnnotations,
K2JVMCompilerArguments::noOptimizedCallableReferences,
K2JVMCompilerArguments::noKotlinNothingValueException,
K2JVMCompilerArguments::noResetJarTimestamps,
K2JVMCompilerArguments::noUnifiedNullChecks,
K2JVMCompilerArguments::useOldSpilledVarTypeAnalysis,
K2JVMCompilerArguments::useOldInlineClassesManglingScheme,
K2JVMCompilerArguments::enableJvmPreview,
)
private val k2JVMCompilerArgumentsStringProperties = commonCompilerArgumentsStringProperties + listOf(
K2JVMCompilerArguments::destination,
K2JVMCompilerArguments::classpath,
K2JVMCompilerArguments::jdkHome,
K2JVMCompilerArguments::expression,
K2JVMCompilerArguments::moduleName,
K2JVMCompilerArguments::jvmTarget,
K2JVMCompilerArguments::abiStability,
K2JVMCompilerArguments::javaModulePath,
K2JVMCompilerArguments::constructorCallNormalizationMode,
K2JVMCompilerArguments::assertionsMode,
K2JVMCompilerArguments::buildFile,
K2JVMCompilerArguments::declarationsOutputPath,
K2JVMCompilerArguments::javaPackagePrefix,
K2JVMCompilerArguments::supportCompatqualCheckerFrameworkAnnotations,
K2JVMCompilerArguments::jspecifyAnnotations,
K2JVMCompilerArguments::jvmDefault,
K2JVMCompilerArguments::defaultScriptExtension,
K2JVMCompilerArguments::stringConcat,
K2JVMCompilerArguments::klibLibraries,
K2JVMCompilerArguments::profileCompilerCommand,
K2JVMCompilerArguments::repeatCompileModules
)
private val k2JVMCompilerArgumentsArrayArgumentProperties = commonCompilerArgumentsArrayArgumentProperties + listOf(
K2JVMCompilerArguments::scriptTemplates,
K2JVMCompilerArguments::additionalJavaModules,
K2JVMCompilerArguments::scriptResolverEnvironment,
K2JVMCompilerArguments::javacArguments,
K2JVMCompilerArguments::javaSourceRoots,
K2JVMCompilerArguments::jsr305,
K2JVMCompilerArguments::friendPaths
)
private val k2MetadataCompilerArgumentsFlagProperties = commonCompilerArgumentsFlagProperties + listOf(
K2MetadataCompilerArguments::enabledInJps
)
private val k2MetadataCompilerArgumentsStringProperties = commonCompilerArgumentsStringProperties + listOf(
K2MetadataCompilerArguments::destination,
K2MetadataCompilerArguments::classpath,
K2MetadataCompilerArguments::moduleName
)
private val k2MetadataCompilerArgumentsArrayProperties = commonCompilerArgumentsArrayArgumentProperties + listOf(
K2MetadataCompilerArguments::friendPaths,
K2MetadataCompilerArguments::refinesPaths
)
private val k2JSCompilerArgumentsFlagProperties = commonCompilerArgumentsFlagProperties + listOf(
K2JSCompilerArguments::noStdlib,
K2JSCompilerArguments::sourceMap,
K2JSCompilerArguments::metaInfo,
K2JSCompilerArguments::irProduceKlibDir,
K2JSCompilerArguments::irProduceKlibFile,
K2JSCompilerArguments::irProduceJs,
K2JSCompilerArguments::irDce,
K2JSCompilerArguments::irDceDriven,
K2JSCompilerArguments::irDcePrintReachabilityInfo,
K2JSCompilerArguments::irPropertyLazyInitialization,
K2JSCompilerArguments::irOnly,
K2JSCompilerArguments::irPerModule,
K2JSCompilerArguments::generateDts,
K2JSCompilerArguments::typedArrays,
K2JSCompilerArguments::friendModulesDisabled,
K2JSCompilerArguments::metadataOnly,
K2JSCompilerArguments::enableJsScripting,
K2JSCompilerArguments::fakeOverrideValidator,
K2JSCompilerArguments::wasm
)
private val k2JSCompilerArgumentsStringProperties = commonCompilerArgumentsStringProperties + listOf(
K2JSCompilerArguments::outputFile,
K2JSCompilerArguments::libraries,
K2JSCompilerArguments::sourceMapPrefix,
K2JSCompilerArguments::sourceMapBaseDirs,
K2JSCompilerArguments::sourceMapEmbedSources,
K2JSCompilerArguments::target,
K2JSCompilerArguments::moduleKind,
K2JSCompilerArguments::main,
K2JSCompilerArguments::outputPrefix,
K2JSCompilerArguments::outputPostfix,
K2JSCompilerArguments::irModuleName,
K2JSCompilerArguments::includes,
K2JSCompilerArguments::friendModules,
K2JSCompilerArguments::errorTolerancePolicy,
)
private val k2JSDceCompilerArgumentsFlagProperties = commonToolArgumentsFlagProperties + listOf(
K2JSDceArguments::devMode,
K2JSDceArguments::printReachabilityInfo,
)
private val k2JSDceCompilerArgumentsStringProperties = listOf(
K2JSDceArguments::outputDirectory,
K2JSDceArguments::devModeOverwritingStrategy,
)
private val k2JSDceCompilerArgumentsArrayProperties = listOf(
K2JSDceArguments::declarationsToKeep
)
private fun <T> assertContentEquals(expect: Iterable<T>, actual: Iterable<T>, preprocessor: Iterable<T>.() -> Iterable<T>) {
(expect.count() == actual.count()) && preprocessor(expect).zip(preprocessor(actual)).all { it.first == it.second }
}
}
}
@@ -0,0 +1,169 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.arguments
import org.jdom.Element
import org.jetbrains.kotlin.cli.common.arguments.*
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.junit.Test
import kotlin.random.Random
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
class CompilerArgumentsSerializationTest {
@Test
fun testDummyJVM() {
doSerializeDeserializeAndCompareTest<K2JVMCompilerArguments>()
}
@Test
fun testRandomFlagArgumentsJVM() {
doRandomFlagArgumentsTest<K2JVMCompilerArguments>()
}
@Test
fun testRandomStringArgumentsJVM() {
doRandomStringArgumentsTest<K2JVMCompilerArguments>()
}
@Test
fun testLongClasspathArgumentJVM() {
doSerializeDeserializeAndCompareTest<K2JVMCompilerArguments> {
classpath = generateSequence { generateRandomString(50) }.take(10).toList().joinToString(File.pathSeparator)
}
}
@Test
fun testRandomArrayArgumentsJVM() {
doRandomArrayArgumentsTest<K2JVMCompilerArguments>()
}
@Test
fun testDummyJs() {
doSerializeDeserializeAndCompareTest<K2JSCompilerArguments>()
}
@Test
fun testRandomFlagArgumentsJS() {
doRandomFlagArgumentsTest<K2JSCompilerArguments>()
}
@Test
fun testRandomStringArgumentsJS() {
doRandomStringArgumentsTest<K2JSCompilerArguments>()
}
@Test
fun testRandomArrayArgumentsJS() {
doRandomArrayArgumentsTest<K2JSCompilerArguments>()
}
@Test
fun testDummyMetadata() {
doSerializeDeserializeAndCompareTest<K2MetadataCompilerArguments>()
}
@Test
fun testRandomFlagArgumentsMetadata() {
doRandomFlagArgumentsTest<K2MetadataCompilerArguments>()
}
@Test
fun testRandomStringArgumentsMetadata() {
doRandomStringArgumentsTest<K2MetadataCompilerArguments>()
}
@Test
fun testLongClasspathArgumentMetadata() {
doSerializeDeserializeAndCompareTest<K2MetadataCompilerArguments> {
classpath = generateSequence { generateRandomString(50) }.take(10).toList().joinToString(File.pathSeparator)
}
}
@Test
fun testRandomArrayArgumentsMetadata() {
doRandomArrayArgumentsTest<K2MetadataCompilerArguments>()
}
@Test
fun testDummyJsDce() {
doSerializeDeserializeAndCompareTest<K2JSDceArguments>()
}
@Test
fun testRandomFlagArgumentsJSDce() {
doRandomFlagArgumentsTest<K2JSDceArguments>()
}
@Test
fun testRandomStringArgumentsJSDce() {
doRandomStringArgumentsTest<K2JSDceArguments>()
}
@Test
fun testRandomArrayArgumentsJSDce() {
doRandomArrayArgumentsTest<K2JSDceArguments>()
}
private inline fun <reified T : CommonToolArguments> doSerializeDeserializeAndCompareTest(configure: T.() -> Unit = {}) {
val oldInstance = T::class.java.getConstructor().newInstance().apply(configure)
val serializer = CompilerArgumentsSerializerV5<T>(oldInstance)
val mockFacetElement = Element("ROOT")
val element = serializer.serializeTo(mockFacetElement)
val newInstance = T::class.java.getConstructor().newInstance()
val deserializer = CompilerArgumentsDeserializerV5(newInstance)
deserializer.deserializeFrom(element)
T::class.memberProperties.mapNotNull { it.safeAs<KProperty1<T, *>>() }.forEach {
assert(it.get(oldInstance) == it.get(newInstance)) {
"Property ${it.name} has different values before (${it.get(oldInstance)}) and after (${it.get(newInstance)}) serialization"
}
}
}
private inline fun <reified T : CommonToolArguments> doRandomFlagArgumentsTest() {
val flagProperties = CompilerArgumentsContentProspector.getFlagCompilerArgumentProperties(T::class)
val randomFlags = generateSequence { Random.nextBoolean() }.take(flagProperties.size).toList()
doSerializeDeserializeAndCompareTest<T> {
flagProperties.zip(randomFlags).forEach {
it.first.cast<KMutableProperty1<T, Boolean>>().set(this, it.second)
}
}
}
private inline fun <reified T : CommonToolArguments> doRandomStringArgumentsTest() {
val stringProperties = CompilerArgumentsContentProspector.getStringCompilerArgumentProperties(T::class)
val randomStrings = generateSequence { generateRandomString(Random.nextInt(20)) }.take(stringProperties.size).toList()
doSerializeDeserializeAndCompareTest<T> {
stringProperties.zip(randomStrings).forEach {
it.first.cast<KMutableProperty1<T, String?>>().set(this, it.second)
}
}
}
private inline fun <reified T : CommonToolArguments> doRandomArrayArgumentsTest() {
val arrayProperties = CompilerArgumentsContentProspector.getArrayCompilerArgumentProperties(T::class)
val randomArrays = generateSequence {
generateSequence { generateRandomString(Random.nextInt(20)) }.take(Random.nextInt(10)).toList().toTypedArray()
}.take(arrayProperties.size).toList()
doSerializeDeserializeAndCompareTest<T> {
arrayProperties.zip(randomArrays).forEach {
it.first.cast<KMutableProperty1<T, Array<String>?>>().set(this, it.second)
}
}
}
companion object {
private val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
private fun generateRandomString(length: Int) = generateSequence { Random.nextInt(0, charPool.size) }
.take(length)
.map(charPool::get)
.joinToString("")
}
}