Kotlin Facet: Get rid of copy constructors for compiler arguments/settings and use reflection-based copying instead

This commit is contained in:
Alexey Sedunov
2016-10-27 15:52:41 +03:00
parent a2948a624f
commit d0de9dd43c
7 changed files with 89 additions and 115 deletions
@@ -20,7 +20,6 @@ import com.intellij.util.SmartList;
import com.sampullara.cli.Argument;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
public abstract class CommonCompilerArguments {
@@ -73,44 +72,11 @@ public abstract class CommonCompilerArguments {
public List<String> unknownExtraFlags = new SmartList<String>();
public CommonCompilerArguments() {
}
protected CommonCompilerArguments(CommonCompilerArguments arguments) {
this.languageVersion = arguments.languageVersion;
this.apiVersion = arguments.apiVersion;
this.suppressWarnings = arguments.suppressWarnings;
this.verbose = arguments.verbose;
this.version = arguments.version;
this.help = arguments.help;
this.extraHelp = arguments.extraHelp;
this.noInline = arguments.noInline;
this.repeat = arguments.repeat;
this.pluginClasspaths = arguments.pluginClasspaths != null ? Arrays.copyOf(arguments.pluginClasspaths, arguments.pluginClasspaths.length) : null;
this.pluginOptions = arguments.pluginOptions != null ? Arrays.copyOf(arguments.pluginOptions, arguments.pluginOptions.length) : null;
this.freeArgs.addAll(arguments.freeArgs);
this.unknownExtraFlags.addAll(arguments.unknownExtraFlags);
}
public abstract CommonCompilerArguments copy();
@NotNull
public String executableScriptFileName() {
return "kotlinc";
}
// Used only for serialize and deserialize settings. Don't use in other places!
public static final class DummyImpl extends CommonCompilerArguments {
public DummyImpl() {
}
public DummyImpl(DummyImpl arguments) {
super(arguments);
}
@Override
public CommonCompilerArguments copy() {
return new DummyImpl(this);
}
}
public static final class DummyImpl extends CommonCompilerArguments {}
}
@@ -20,8 +20,6 @@ import com.sampullara.cli.Argument;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.CALL;
import static org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.NO_CALL;
@@ -75,29 +73,6 @@ public class K2JSCompilerArguments extends CommonCompilerArguments {
@ValueDescription("<path>")
public String outputPostfix;
public K2JSCompilerArguments() {
}
private K2JSCompilerArguments(K2JSCompilerArguments arguments) {
super(arguments);
this.outputFile = arguments.outputFile;
this.noStdlib = arguments.noStdlib;
this.libraryFiles = arguments.libraryFiles != null ? Arrays.copyOf(arguments.libraryFiles, arguments.libraryFiles.length) : null;
this.sourceMap = arguments.sourceMap;
this.metaInfo = arguments.metaInfo;
this.kjsm = arguments.kjsm;
this.target = arguments.target;
this.moduleKind = arguments.moduleKind;
this.main = arguments.main;
this.outputPrefix = arguments.outputPrefix;
this.outputPostfix = arguments.outputPostfix;
}
@Override
public K2JSCompilerArguments copy() {
return new K2JSCompilerArguments(this);
}
@Override
@NotNull
public String executableScriptFileName() {
@@ -110,12 +110,6 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
// Paths to output directories for friend modules.
public String[] friendPaths;
@Override
public CommonCompilerArguments copy() {
// No need to copy these arguments yet
throw new UnsupportedOperationException();
}
@Override
@NotNull
public String executableScriptFileName() {
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.common.arguments
import com.intellij.util.xmlb.XmlSerializerUtil
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.util.*
fun <T : Any> copyBean(bean: T) = copyFields(bean, bean.javaClass.newInstance(), true)
fun <From : Any, To : From> mergeBeans(from: From, to: To): To {
// TODO: rewrite when updated version of com.intellij.util.xmlb is available on TeamCity
return copyFields(from, XmlSerializerUtil.createCopy(to), false)
}
private fun <From : Any, To : From> copyFields(from: From, to: To, deepCopyWhenNeeded: Boolean = false): To {
val fromFields = collectFieldsToCopy(from.javaClass)
for (fromField in fromFields) {
val toField = to.javaClass.getField(fromField.name)
val fromValue = fromField.get(from)
toField.set(to, if (deepCopyWhenNeeded) fromValue?.copyValueIfNeeded() else fromValue)
}
return to
}
private fun Any.copyValueIfNeeded(): Any {
@Suppress("UNCHECKED_CAST")
return when (this) {
is ByteArray -> Arrays.copyOf(this, size)
is CharArray -> Arrays.copyOf(this, size)
is ShortArray -> Arrays.copyOf(this, size)
is IntArray -> Arrays.copyOf(this, size)
is LongArray -> Arrays.copyOf(this, size)
is FloatArray -> Arrays.copyOf(this, size)
is DoubleArray -> Arrays.copyOf(this, size)
is BooleanArray -> Arrays.copyOf(this, size)
is Array<*> -> Array(size) { this[it]?.copyValueIfNeeded() }
is MutableCollection<*> -> (this as Collection<Any?>).mapTo(javaClass.newInstance() as MutableCollection<Any?>) { it?.copyValueIfNeeded() }
is MutableMap<*, *> -> (javaClass.newInstance() as MutableMap<Any?, Any?>).apply {
for ((k, v) in this@copyValueIfNeeded.entries) {
put(k?.copyValueIfNeeded(), v?.copyValueIfNeeded())
}
}
else -> this
}
}
private fun collectFieldsToCopy(clazz: Class<*>): List<Field> {
val fromFields = ArrayList<Field>()
var currentClass: Class<*>? = clazz
while (currentClass != null) {
for (field in currentClass.declaredFields) {
val modifiers = field.modifiers
if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
fromFields.add(field)
}
}
currentClass = currentClass.superclass
}
return fromFields
}
@@ -23,16 +23,6 @@ class CompilerSettings {
var copyJsLibraryFiles: Boolean = true
var outputDirectoryForJsLibraryFiles: String = DEFAULT_OUTPUT_DIRECTORY
constructor()
constructor(settings: CompilerSettings) {
additionalArguments = settings.additionalArguments
scriptTemplates = settings.scriptTemplates
scriptTemplatesClasspath = settings.scriptTemplatesClasspath
copyJsLibraryFiles = settings.copyJsLibraryFiles
outputDirectoryForJsLibraryFiles = settings.outputDirectoryForJsLibraryFiles
}
companion object {
private val DEFAULT_ADDITIONAL_ARGUMENTS = "-version"
private val DEFAULT_OUTPUT_DIRECTORY = "lib"
@@ -23,6 +23,7 @@ import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModel
import com.intellij.util.text.VersionComparatorUtil
import org.jetbrains.kotlin.cli.common.arguments.copyBean
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumentsHolder
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
@@ -115,15 +116,15 @@ internal fun KotlinFacetSettings.initializeIfNeeded(module: Module, rootModel: M
with(compilerInfo) {
if (commonCompilerArguments == null) {
commonCompilerArguments = KotlinCommonCompilerArgumentsHolder.getInstance(project).settings.copy()
commonCompilerArguments = copyBean(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings)
}
if (compilerSettings == null) {
compilerSettings = CompilerSettings(KotlinCompilerSettings.getInstance(project).settings)
compilerSettings = copyBean(KotlinCompilerSettings.getInstance(project).settings)
}
if (k2jsCompilerArguments == null) {
k2jsCompilerArguments = Kotlin2JsCompilerArgumentsHolder.getInstance (project).settings.copy()
k2jsCompilerArguments = copyBean(Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings)
}
}
}
@@ -16,33 +16,29 @@
package org.jetbrains.kotlin.compilerRunner
import com.intellij.util.xmlb.XmlSerializerUtil
import org.jetbrains.jps.api.GlobalOptions
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.mergeBeans
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
import org.jetbrains.kotlin.config.CompilerSettings
import org.jetbrains.kotlin.daemon.client.CompilationServices
import org.jetbrains.kotlin.daemon.client.DaemonReportMessage
import org.jetbrains.kotlin.daemon.client.DaemonReportingTargets
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
import org.jetbrains.kotlin.config.CompilerSettings
import org.jetbrains.kotlin.daemon.common.*
import org.jetbrains.kotlin.jps.build.KotlinBuilder
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.utils.rethrow
import java.io.*
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import java.rmi.ConnectException
import java.util.*
import java.util.concurrent.TimeUnit
@@ -250,36 +246,6 @@ object KotlinCompilerRunner {
}
}
private fun <T : CommonCompilerArguments> mergeBeans(from: CommonCompilerArguments, to: T): T {
// TODO: rewrite when updated version of com.intellij.util.xmlb is available on TeamCity
val copy = XmlSerializerUtil.createCopy(to)
val fromFields = collectFieldsToCopy(from.javaClass)
for (fromField in fromFields) {
val toField = copy.javaClass.getField(fromField.name)
toField.set(copy, fromField.get(from))
}
return copy
}
private fun collectFieldsToCopy(clazz: Class<*>): List<Field> {
val fromFields = ArrayList<Field>()
var currentClass: Class<*>? = clazz
while (currentClass != null) {
for (field in currentClass.declaredFields) {
val modifiers = field.modifiers
if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
fromFields.add(field)
}
}
currentClass = currentClass.superclass
}
return fromFields
}
private fun setupK2JvmArguments(moduleFile: File, settings: K2JVMCompilerArguments) {
with(settings) {
module = moduleFile.absolutePath