diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java index 1d28575724a..5c1534a1b7e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/DescriptorAsmUtil.java @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.codegen; -import com.google.common.collect.Sets; import com.intellij.openapi.util.Pair; import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; @@ -52,7 +51,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import java.util.ArrayList; import java.util.List; -import java.util.Set; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isPrimitiveClass; @@ -495,12 +493,12 @@ public class DescriptorAsmUtil { return index; } - public static void genInvokeAppendMethod(@NotNull StringAppendGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) { + public static void genInvokeAppendMethod(@NotNull StringConcatGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType) { genInvokeAppendMethod(generator, type, kotlinType, null); } public static void genInvokeAppendMethod( - @NotNull StringAppendGenerator generator, + @NotNull StringConcatGenerator generator, @NotNull Type type, @Nullable KotlinType kotlinType, @Nullable KotlinTypeMapper typeMapper diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 874f14aa073..0877e64f963 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -925,7 +925,7 @@ public class ExpressionCodegen extends KtVisitor impleme } else { return StackValue.operation(type, v -> { - StringAppendGenerator generator = StringAppendGenerator.Companion.create(state, v); + StringConcatGenerator generator = StringConcatGenerator.Companion.create(state, v); generator.genStringBuilderConstructorIfNeded(); invokeAppendForEntries(generator, entries); generator.genToString(); @@ -934,7 +934,7 @@ public class ExpressionCodegen extends KtVisitor impleme } } - private void invokeAppendForEntries(StringAppendGenerator generator, List entries) { + private void invokeAppendForEntries(StringConcatGenerator generator, List entries) { for (StringTemplateEntry entry : entries) { if (entry instanceof StringTemplateEntry.Expression) { invokeAppend(generator, ((StringTemplateEntry.Expression) entry).expression); @@ -4300,7 +4300,7 @@ public class ExpressionCodegen extends KtVisitor impleme } } - public void invokeAppend(StringAppendGenerator generator, KtExpression expr) { + public void invokeAppend(StringConcatGenerator generator, KtExpression expr) { expr = KtPsiUtil.safeDeparenthesize(expr); ConstantValue compileTimeConstant = getPrimitiveOrStringCompileTimeConstant(expr); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java index c14cf81bdda..61c4c9b191e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionsFromAnyGeneratorImpl.java @@ -102,7 +102,7 @@ public class FunctionsFromAnyGeneratorImpl extends FunctionsFromAnyGenerator { mv.visitCode(); - StringAppendGenerator generator = StringAppendGenerator.Companion.create(generationState, iv); + StringConcatGenerator generator = StringConcatGenerator.Companion.create(generationState, iv); generator.genStringBuilderConstructorIfNeded(); boolean first = true; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt similarity index 90% rename from compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt rename to compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt index 80de04dba73..fc5f7a4bb74 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StringAppendGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StringConcatGenerator.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.codegen import com.google.common.collect.Sets import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.JvmRuntimeStringConcat import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE import org.jetbrains.org.objectweb.asm.Handle @@ -16,14 +16,14 @@ import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import java.lang.StringBuilder -class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAdapter) { +class StringConcatGenerator(val mode: JvmRuntimeStringConcat, val mv: InstructionAdapter) { private val template = StringBuilder("") private val paramTypes = arrayListOf() @JvmOverloads fun genStringBuilderConstructorIfNeded(swap: Boolean = false) { - if (useInvokeDynamic) return + if (mode.isDynamic) return mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder") mv.dup() mv.invokespecial("java/lang/StringBuilder", "", "()V", false) @@ -33,7 +33,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd } fun addCharConstant(value: Char) { - if (!useInvokeDynamic) { + if (!mode.isDynamic) { mv.iconst(value.toInt()) invokeAppend(Type.CHAR_TYPE) } else { @@ -42,7 +42,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd } fun addStringConstant(value: String) { - if (!useInvokeDynamic) { + if (!mode.isDynamic) { mv.aconst(value) invokeAppend(JAVA_STRING_TYPE) } else { @@ -51,7 +51,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd } fun invokeAppend(type: Type) { - if (!useInvokeDynamic) { + if (!mode.isDynamic) { mv.invokevirtual( "java/lang/StringBuilder", "append", @@ -71,7 +71,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd } fun genToString() { - if (!useInvokeDynamic) { + if (!mode.isDynamic) { mv.invokevirtual("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false) } else { //if state was flushed in `invokeAppend` do nothing @@ -114,7 +114,7 @@ class StringAppendGenerator(val useInvokeDynamic: Boolean, val mv: InstructionAd } fun create(state: GenerationState, mv: InstructionAdapter) = - StringAppendGenerator(/*TODO: add flag*/state.target.bytecodeVersion >= JvmTarget.JVM_9.bytecodeVersion, mv) + StringConcatGenerator(state.runtimeStringConcat, mv) } } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt index 57ed6b00728..4debbeb4382 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/Concat.kt @@ -39,7 +39,7 @@ class Concat : IntrinsicMethod() { arguments: List, receiver: StackValue ): Type { - val generator = StringAppendGenerator.create(codegen.state, v) + val generator = StringConcatGenerator.create(codegen.state, v) if (element is KtBinaryExpression && element.operationReference.getReferencedNameElementType() == KtTokens.PLUS) { // LHS + RHS generator.genStringBuilderConstructorIfNeded() @@ -59,7 +59,7 @@ class Concat : IntrinsicMethod() { override fun toCallable(method: CallableMethod): Callable = object : IntrinsicCallable(method) { - lateinit var generator: StringAppendGenerator + lateinit var generator: StringConcatGenerator override fun invokeMethodWithArguments( resolvedCall: ResolvedCall<*>, receiver: StackValue, @@ -83,8 +83,8 @@ class Concat : IntrinsicMethod() { } override fun afterReceiverGeneration(v: InstructionAdapter, frameMap: FrameMap, state: GenerationState) { - generator = StringAppendGenerator.create(state, v) - if (!generator.useInvokeDynamic) { + generator = StringConcatGenerator.create(state, v) + if (!generator.mode.isDynamic) { v.generateNewInstanceDupAndPlaceBeforeStackTop(frameMap, JAVA_STRING_TYPE, "java/lang/StringBuilder") v.invokespecial("java/lang/StringBuilder", "", "(Ljava/lang/String;)V", false) } else { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/StringPlus.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/StringPlus.kt index 51162b879f3..45ad737ca7b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/StringPlus.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/StringPlus.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.codegen.intrinsics import org.jetbrains.kotlin.codegen.* -import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -25,19 +24,19 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class StringPlus : IntrinsicMethod() { override fun toCallable(method: CallableMethod): Callable = object : IntrinsicCallable(method) { - private lateinit var generator: StringAppendGenerator + private lateinit var generator: StringConcatGenerator override fun invokeMethodWithArguments( resolvedCall: ResolvedCall<*>, receiver: StackValue, codegen: ExpressionCodegen ): StackValue { - generator = StringAppendGenerator.create(codegen.state, codegen.v) + generator = StringConcatGenerator.create(codegen.state, codegen.v) return super.invokeMethodWithArguments(resolvedCall, receiver, codegen) } override fun genInvokeInstruction(v: InstructionAdapter) { - if (!generator.useInvokeDynamic) { + if (!generator.mode.isDynamic) { v.invokestatic( IntrinsicMethods.INTRINSICS_CLASS_NAME, "stringPlus", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;", false diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 8de52025552..87549650be1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -191,6 +191,10 @@ class GenerationState private constructor( val languageVersionSettings = configuration.languageVersionSettings val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT + val runtimeStringConcat = + if (target.bytecodeVersion >= JvmTarget.JVM_9.bytecodeVersion) + configuration.get(JVMConfigurationKeys.RUNTIME_STRING_CONCAT) ?: JvmRuntimeStringConcat.DISABLE + else JvmRuntimeStringConcat.DISABLE val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module) val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 53e323c535c..6fd6fb15d06 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -334,6 +334,16 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { ) var emitJvmTypeAnnotations: Boolean by FreezableVar(false) + @Argument( + value = "-Xruntime-string-concat", + valueDescription = "{disable|enable|indy}", + description = """Switch a way in which string concatenation is performed. +-Xruntime-string-concat=enable Performs string concatenation via `invokedynamic` 'makeConcatWithConstants'. Works only with `-jvm-target 9` or greater +-Xruntime-string-concat=indy Performs string concatenation via `invokedynamic` 'makeConcat'. Works only with `-jvm-target 9` or greater +-Xruntime-string-concat=disable Performs string concatenation via `StringBuilder`""" + ) + var runtimeStringConcat: String? by NullableStringFreezableVar(JvmRuntimeStringConcat.DISABLE.name.toLowerCase()) + @Argument( value = "-Xklib", valueDescription = "", diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index eac86b26b10..7af288eb305 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -49,6 +49,24 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu } } + if (arguments.runtimeStringConcat != null) { + val runtimeStringConcat = JvmRuntimeStringConcat.fromString(arguments.runtimeStringConcat!!) + if (runtimeStringConcat != null) { + put(JVMConfigurationKeys.RUNTIME_STRING_CONCAT, runtimeStringConcat) + if (jvmTarget.bytecodeVersion < JvmTarget.JVM_9.bytecodeVersion && runtimeStringConcat != JvmRuntimeStringConcat.DISABLE) { + messageCollector.report( + WARNING, + "`-Xruntime-string-concat=${arguments.runtimeStringConcat}` does nothing with JVM target `${jvmTarget.description}`." + ) + } + } else { + messageCollector.report( + ERROR, "Unknown `runtime-string-concat` mode: ${arguments.jvmTarget}\n" + + "Supported versions: ${JvmRuntimeStringConcat.values().joinToString { it.name.toLowerCase() }}" + ) + } + } + addAll(JVMConfigurationKeys.ADDITIONAL_JAVA_MODULES, arguments.additionalJavaModules?.asList()) } diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 4f736403709..13000fccced 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -111,6 +111,9 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey EMIT_JVM_TYPE_ANNOTATIONS = CompilerConfigurationKey.create("Emit JVM type annotations in bytecode"); + public static final CompilerConfigurationKey RUNTIME_STRING_CONCAT = + CompilerConfigurationKey.create("Specifies string concatenation scheme"); + public static final CompilerConfigurationKey> KLIB_PATHS = CompilerConfigurationKey.create("Paths to .klib libraries"); diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmRuntimeStringConcat.kt b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmRuntimeStringConcat.kt new file mode 100644 index 00000000000..94a21b27480 --- /dev/null +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmRuntimeStringConcat.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2020 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.config + +enum class JvmRuntimeStringConcat { + DISABLE, + ENABLE, // makeConcatWithConstants + INDY; // makeConcat + + val isDynamic + get() = this != DISABLE + + companion object { + @JvmStatic + fun fromString(string: String) = values().find { it.name == string.toUpperCase() } + } +} \ No newline at end of file diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index abcca45ce2b..bcf80c1b924 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -81,6 +81,11 @@ where advanced options include: profilerPath is a path to libasyncProfiler.so Example: -Xprofile=/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000: -Xrepeat= Debug option: Repeats modules compilation times + -Xruntime-string-concat={disable|enable|indy} + Switch a way in which string concatenation is performed. + -Xruntime-string-concat=enable Performs string concatenation via `invokedynamic` 'makeConcatWithConstants'. Works only with `-jvm-target 9` or greater + -Xruntime-string-concat=indy Performs string concatenation via `invokedynamic` 'makeConcat'. Works only with `-jvm-target 9` or greater + -Xruntime-string-concat=disable Performs string concatenation via `StringBuilder` -Xsanitize-parentheses Transform '(' and ')' in method names to some other character sequence. This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used to workaround problems with parentheses in identifiers on certain platforms diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic.kt index 5051d31e71b..2cc2e211755 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 class A diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt index 49ff0fa31a1..e967c489bcf 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic200.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 fun box() { diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic201.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic201.kt index 56caae49925..3c66aae3184 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic201.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamic201.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 fun box() { val z = "0" diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt index a93ca9e3c09..a3b0336fef5 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatDynamicConstants.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 class A diff --git a/compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt b/compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt index e8a7ff8f369..74a1decb9a2 100644 --- a/compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt +++ b/compiler/testData/codegen/bytecodeText/stringOperations/concatNotDynamic.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 fun box(a: String, b: String?) { val sb = StringBuilder(); diff --git a/compiler/testData/codegen/java9/box/concatDynamic200.kt b/compiler/testData/codegen/java9/box/concatDynamic200.kt index 4f20cb626ff..8628b545d87 100644 --- a/compiler/testData/codegen/java9/box/concatDynamic200.kt +++ b/compiler/testData/codegen/java9/box/concatDynamic200.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 fun box(): String { val z = "0" diff --git a/compiler/testData/codegen/java9/box/concatDynamic201.kt b/compiler/testData/codegen/java9/box/concatDynamic201.kt index 5efc7095305..12e7fb0b267 100644 --- a/compiler/testData/codegen/java9/box/concatDynamic201.kt +++ b/compiler/testData/codegen/java9/box/concatDynamic201.kt @@ -1,3 +1,4 @@ +// KOTLIN_CONFIGURATION_FLAGS: RUNTIME_STRING_CONCAT=enable // JVM_TARGET: 9 fun box(): String { val z = "0" diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt index c93a3ca1a6e..57ebd904df2 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/test/KotlinBaseTest.kt @@ -157,6 +157,7 @@ abstract class KotlinBaseTest : KtUsefulTestCase() "CONSTRUCTOR_CALL_NORMALIZATION_MODE=([a-zA-Z_\\-0-9]*)" ) private val ASSERTIONS_MODE_FLAG_PATTERN = Pattern.compile("ASSERTIONS_MODE=([a-zA-Z_0-9-]*)") + private val RUNTIME_STRING_CONCAT = Pattern.compile("RUNTIME_STRING_CONCAT=([a-zA-Z_0-9-]*)") private fun tryApplyBooleanFlag( configuration: CompilerConfiguration, @@ -294,6 +295,14 @@ abstract class KotlinBaseTest : KtUsefulTestCase() ?: error("Wrong ASSERTIONS_MODE value: $flagValueString") configuration.put(JVMConfigurationKeys.ASSERTIONS_MODE, mode) } + + m = RUNTIME_STRING_CONCAT.matcher(flag) + if (m.matches()) { + val flagValueString = m.group(1) + val mode = JvmRuntimeStringConcat.fromString(flagValueString) + ?: error("Wrong RUNTIME_STRING_CONCAT value: $flagValueString") + configuration.put(JVMConfigurationKeys.RUNTIME_STRING_CONCAT, mode) + } } }