Fix reflection-based operations on compiler arguments after conversion
This commit is contained in:
+4
-2
@@ -23,6 +23,7 @@ import com.intellij.util.xmlb.XmlSerializer
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
import org.jetbrains.kotlin.config.SettingConstants
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
abstract class BaseKotlinCompilerSettings<T : Any> protected constructor() : PersistentStateComponent<Element>, Cloneable {
|
||||
@Suppress("LeakingThis")
|
||||
@@ -40,9 +41,10 @@ abstract class BaseKotlinCompilerSettings<T : Any> protected constructor() : Per
|
||||
}
|
||||
|
||||
protected fun validateInheritedFieldsUnchanged(settings: T) {
|
||||
val inheritedFields = collectFieldsToCopy(settings.javaClass, true)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val inheritedProperties = collectProperties<T>(settings::class as KClass<T>, true)
|
||||
val defaultInstance = createSettings()
|
||||
val invalidFields = inheritedFields.filter { it.get(settings) != it.get(defaultInstance) }
|
||||
val invalidFields = inheritedProperties.filter { it.get(settings) != it.get(defaultInstance) }
|
||||
if (invalidFields.isNotEmpty()) {
|
||||
throw IllegalArgumentException("Following fields are expected to be left unchanged in ${settings.javaClass}: ${invalidFields.joinToString { it.name }}")
|
||||
}
|
||||
|
||||
@@ -17,11 +17,18 @@
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.xmlb.SerializationFilter
|
||||
import com.intellij.util.xmlb.SkipDefaultsSerializationFilter
|
||||
import com.intellij.util.xmlb.XmlSerializer
|
||||
import org.jdom.DataConversionException
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.kotlin.cli.common.arguments.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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 }
|
||||
|
||||
@@ -166,6 +173,50 @@ fun CompilerSettings.convertPathsToSystemIndependent() {
|
||||
outputDirectoryForJsLibraryFiles = PathUtil.toSystemIndependentName(outputDirectoryForJsLibraryFiles)
|
||||
}
|
||||
|
||||
private fun KClass<*>.superClass() = superclasses.firstOrNull { !it.java.isInterface }
|
||||
|
||||
private fun Class<*>.computeNormalPropertyOrdering(): Map<String, Int> {
|
||||
val result = LinkedHashMap<String, Int>()
|
||||
var count = 0
|
||||
generateSequence(this) { it.superclass }.forEach { clazz ->
|
||||
for (method in clazz.declaredMethods) {
|
||||
if (method.modifiers and Modifier.STATIC != 0) continue
|
||||
|
||||
val name = method.name
|
||||
if (!JvmAbi.isGetterName(name)) continue
|
||||
|
||||
val propertyName = propertyNameByGetMethodName(Name.identifier(name))?.asString() ?: continue
|
||||
|
||||
result[propertyName] = count++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private val allNormalOrderings = HashMap<Class<*>, Map<String, Int>>()
|
||||
|
||||
private val Class<*>.normalOrdering
|
||||
get() = allNormalOrderings.getOrPut(this) { computeNormalPropertyOrdering() }
|
||||
|
||||
// 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) {
|
||||
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!!] }
|
||||
.forEachIndexed { index, element -> elementsToReorder[index] = element.clone() }
|
||||
}
|
||||
|
||||
private fun buildChildElement(element: Element, tag: String, bean: Any, filter: SerializationFilter) {
|
||||
Element(tag).apply {
|
||||
XmlSerializer.serializeInto(bean, this, filter)
|
||||
restoreNormalOrdering(bean)
|
||||
element.addContent(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
|
||||
val filter = SkipDefaultsSerializationFilter()
|
||||
|
||||
@@ -177,17 +228,11 @@ private fun KotlinFacetSettings.writeLatestConfig(element: Element) {
|
||||
}
|
||||
compilerSettings?.let { copyBean(it) }?.let {
|
||||
it.convertPathsToSystemIndependent()
|
||||
Element("compilerSettings").apply {
|
||||
XmlSerializer.serializeInto(it, this, filter)
|
||||
element.addContent(this)
|
||||
}
|
||||
buildChildElement(element, "compilerSettings", it, filter)
|
||||
}
|
||||
compilerArguments?.let { copyBean(it) }?.let {
|
||||
it.convertPathsToSystemIndependent()
|
||||
Element("compilerArguments").apply {
|
||||
XmlSerializer.serializeInto(it, this, filter)
|
||||
element.addContent(this)
|
||||
}
|
||||
buildChildElement(element, "compilerArguments", it, filter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,5 +68,6 @@
|
||||
<orderEntry type="module" module-name="frontend.script" />
|
||||
<orderEntry type="module" module-name="idea-maven" />
|
||||
<orderEntry type="module" module-name="backend.jvm" />
|
||||
<orderEntry type="library" name="kotlin-reflect" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -31,11 +31,11 @@ import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.config.createCompilerArguments
|
||||
import org.jetbrains.kotlin.config.splitArgumentString
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.*
|
||||
import javax.swing.border.EmptyBorder
|
||||
import javax.swing.event.DocumentEvent
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
|
||||
class KotlinFacetEditorGeneralTab(
|
||||
private val configuration: KotlinFacetConfiguration,
|
||||
@@ -172,14 +172,15 @@ class KotlinFacetEditorGeneralTab(
|
||||
is TargetPlatformKind.JavaScript -> jsUIExposedFields
|
||||
else -> commonUIExposedFields
|
||||
}
|
||||
val fieldsToCheck = collectFieldsToCopy(argumentClass, false).filter { it.name in fieldNamesToCheck }
|
||||
|
||||
val propertiesToCheck = collectProperties(argumentClass.kotlin, false).filter { it.name in fieldNamesToCheck }
|
||||
val overridingArguments = ArrayList<String>()
|
||||
val redundantArguments = ArrayList<String>()
|
||||
for (field in fieldsToCheck) {
|
||||
val additionalValue = field[additionalArguments]
|
||||
if (additionalValue != field[emptyArguments]) {
|
||||
val argumentInfo = field.annotations.firstIsInstanceOrNull<Argument>() ?: continue
|
||||
val addTo = if (additionalValue != field[primaryArguments]) overridingArguments else redundantArguments
|
||||
for (property in propertiesToCheck) {
|
||||
val additionalValue = property.get(additionalArguments)
|
||||
if (additionalValue != property.get(emptyArguments)) {
|
||||
val argumentInfo = property.findAnnotation<Argument>() ?: continue
|
||||
val addTo = if (additionalValue != property.get(primaryArguments)) overridingArguments else redundantArguments
|
||||
addTo += "<strong>" + argumentInfo.value.first() + "</strong>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgu
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.versions.*
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
private fun getDefaultTargetPlatform(module: Module, rootModel: ModuleRootModel?): TargetPlatformKind<*> {
|
||||
if (getRuntimeLibraryVersions(module, rootModel, TargetPlatformKind.JavaScript).isNotEmpty()) {
|
||||
@@ -221,7 +221,8 @@ fun parseCompilerArgumentsToFacet(
|
||||
val primaryFields = compilerArguments.primaryFields
|
||||
val ignoredFields = compilerArguments.ignoredFields
|
||||
|
||||
fun exposeAsAdditionalArgument(field: Field) = field.name !in primaryFields && field.get(compilerArguments) != field.get(defaultCompilerArguments)
|
||||
fun exposeAsAdditionalArgument(property: KProperty1<CommonCompilerArguments, Any?>) =
|
||||
property.name !in primaryFields && property.get(compilerArguments) != property.get(defaultCompilerArguments)
|
||||
|
||||
val additionalArgumentsString = with(compilerArguments::class.java.newInstance()) {
|
||||
copyFieldsSatisfying(compilerArguments, this) { exposeAsAdditionalArgument(it) && it.name !in ignoredFields }
|
||||
|
||||
Reference in New Issue
Block a user