From 4911f7820013102813dd0a4ed2a0cdcbaad21c94 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 31 Jan 2017 15:36:21 +0500 Subject: [PATCH] Inner classes (#207) * Inner classes lowering * cmd line fix * tests * * reverted adding parameter to inner classes constructors * variables are identified by their descriptors * review fixes --- .../jetbrains/kotlin/backend/konan/Context.kt | 44 ++++-- .../kotlin/backend/konan/KonanLower.kt | 3 + .../kotlin/backend/konan/KonanPhases.kt | 1 + .../konan/descriptors/DescriptorUtils.kt | 20 +-- .../backend/konan/llvm/VariableManager.kt | 24 +-- .../lower/DefaultParameterStubGenerator.kt | 21 ++- .../backend/konan/lower/InnerClassLowering.kt | 141 ++++++++++++++++++ backend.native/tests/build.gradle | 20 +++ .../tests/codegen/innerClass/generic.kt | 15 ++ .../tests/codegen/innerClass/getOuterVal.kt | 12 ++ .../tests/codegen/innerClass/qualifiedThis.kt | 46 ++++++ .../tests/codegen/innerClass/simple.kt | 12 ++ cmd/konanc | 2 +- 13 files changed, 323 insertions(+), 38 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt create mode 100644 backend.native/tests/codegen/innerClass/generic.kt create mode 100644 backend.native/tests/codegen/innerClass/getOuterVal.kt create mode 100644 backend.native/tests/codegen/innerClass/qualifiedThis.kt create mode 100644 backend.native/tests/codegen/innerClass/simple.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 41b4ed80cf5..e984a0c1f5b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -1,24 +1,46 @@ package org.jetbrains.kotlin.backend.konan -import llvm.* -import org.jetbrains.kotlin.backend.konan.ir.Ir -import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex -import org.jetbrains.kotlin.backend.konan.KonanBackendContext -import org.jetbrains.kotlin.backend.konan.KonanPhase -import org.jetbrains.kotlin.backend.konan.KonanConfig -import org.jetbrains.kotlin.backend.konan.KonanConfigKeys -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.resolve.BindingContext +import llvm.LLVMDumpModule +import llvm.LLVMModuleRef +import org.jetbrains.kotlin.backend.jvm.descriptors.initialize import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint -import org.jetbrains.kotlin.backend.konan.llvm.* +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.Ir +import org.jetbrains.kotlin.backend.konan.llvm.Llvm +import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations +import org.jetbrains.kotlin.backend.konan.llvm.verifyModule +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import java.lang.System.out +internal class SpecialDescriptorsFactory { + private val outerThisDescriptors = mutableMapOf() + + fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor = + if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor") + else outerThisDescriptors.getOrPut(innerClassDescriptor) { + val outerClassDescriptor = DescriptorUtils.getContainingClass(innerClassDescriptor) ?: + throw AssertionError("No containing class for inner class $innerClassDescriptor") + + val receiver = ReceiverParameterDescriptorImpl(innerClassDescriptor, ImplicitClassReceiver(innerClassDescriptor)) + PropertyDescriptorImpl.create(innerClassDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE, + false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE, + false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver) + } +} + internal final class Context(val config: KonanConfig) : KonanBackendContext() { var moduleDescriptor: ModuleDescriptor? = null + val specialDescriptorsFactory = SpecialDescriptorsFactory() + // TODO: make lateinit? var irModule: IrModuleFragment? = null set(module: IrModuleFragment?) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index c7b3ed3aca1..ec5af21dd11 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -20,6 +20,9 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_ENUMS) { EnumClassLowering(context).run(irFile) } + phaser.phase(KonanPhase.LOWER_INNER_CLASSES) { + InnerClassLowering(context).runOnFilePostfix(irFile) + } phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) { DefaultParameterStubGenerator(context).runOnFilePostfix(irFile) DefaultParameterInjector(context).runOnFilePostfix(irFile) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index dd6c3d07253..b089571033d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -19,6 +19,7 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_INLINE("Functions inlining"), /* ... ... */ AUTOBOX("Autoboxing of primitive types"), /* ... ... */ LOWER_ENUMS("Enum classes lowering"), + /* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 4106b161ea8..fabd169b0c6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -134,16 +134,18 @@ internal fun KotlinType.isUnboundCallableReference(): Boolean { internal val CallableDescriptor.allValueParameters: List get() { - val constructorReceiver = if (this is ConstructorDescriptor) { - this.constructedClass.thisAsReceiverParameter - } else { - null - } + val receivers = mutableListOf() - val receivers = listOf( - constructorReceiver, - this.dispatchReceiverParameter, - this.extensionReceiverParameter).filterNotNull() + if (this is ConstructorDescriptor) + receivers.add(this.constructedClass.thisAsReceiverParameter) + + val dispatchReceiverParameter = this.dispatchReceiverParameter + if (dispatchReceiverParameter != null) + receivers.add(dispatchReceiverParameter) + + val extensionReceiverParameter = this.extensionReceiverParameter + if (extensionReceiverParameter != null) + receivers.add(extensionReceiverParameter) return receivers + this.valueParameters } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt index 842477a9551..7b5c5e3e0c3 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/VariableManager.kt @@ -27,8 +27,13 @@ internal class VariableManager(val codegen: CodeGenerator) { override fun toString() = "value of ${value} from ${name}" } + data class ScopedVariable private constructor(val descriptor: ValueDescriptor, val context: CodeContext) + { + constructor(scoped: Pair) : this(scoped.first, scoped.second) + } + val variables: ArrayList = arrayListOf() - val contextVariablesToIndex: HashMap, Int> = hashMapOf() + val contextVariablesToIndex: HashMap = hashMapOf() // Clears inner state of variable manager. fun clear() { @@ -52,11 +57,11 @@ internal class VariableManager(val codegen: CodeGenerator) { fun createMutable(scoped: Pair, isVar: Boolean, value: LLVMValueRef? = null) : Int { val descriptor = scoped.first - val scopedVariable = scoped.first.name to scoped.second + val scopedVariable = ScopedVariable(scoped) assert(!contextVariablesToIndex.contains(scopedVariable)) val index = variables.size val type = codegen.getLLVMType(descriptor.type) - val slot = codegen.alloca(type, descriptor.name.asString()) + val slot = codegen.alloca(type, scopedVariable.descriptor.name.asString()) if (value != null) codegen.storeAnyLocal(value, slot) variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar)) @@ -85,21 +90,18 @@ internal class VariableManager(val codegen: CodeGenerator) { } fun createImmutable(scoped: Pair, value: LLVMValueRef) : Int { - val descriptor = scoped.first - val scopedVariable = scoped.first.name to scoped.second + val scopedVariable = ScopedVariable(scoped) + if(contextVariablesToIndex.containsKey(scopedVariable)) + throw Error("${scopedVariable.descriptor} is already defined") assert(!contextVariablesToIndex.containsKey(scopedVariable)) val index = variables.size - variables.add(ValueRecord(value, descriptor.name)) + variables.add(ValueRecord(value, scopedVariable.descriptor.name)) contextVariablesToIndex[scopedVariable] = index return index } fun indexOf(scoped: Pair) : Int { - return indexOfNamed(scoped.first.name to scoped.second) - } - - private fun indexOfNamed(scoped: Pair) : Int { - return contextVariablesToIndex.getOrElse(scoped) { -1 } + return contextVariablesToIndex.getOrElse(ScopedVariable(scoped)) { -1 } } fun addressOf(index: Int): LLVMValueRef { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt index 509cfcf467d..79a2c36380c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DefaultParameterStubGenerator.kt @@ -75,6 +75,18 @@ class DefaultParameterStubGenerator internal constructor(val context: Context): val params = mutableListOf() val variables = mutableMapOf() + val newExtensionReceiver = + if (description.function.extensionReceiverParameter == null) { + null + } else { + IrGetValueImpl( + startOffset = irFunction.startOffset, + endOffset = irFunction.endOffset, + descriptor = description.function.extensionReceiverParameter!!, + origin = null + ) + } + for (valueParameter in functionDescriptor.valueParameters) { val parameterDescriptor = description.function.valueParameters[valueParameter.index] if (valueParameter.hasDefaultValue()) { @@ -92,6 +104,8 @@ class DefaultParameterStubGenerator internal constructor(val context: Context): /* Use previously calculated values in next expression. */ exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() { override fun visitGetValue(expression: IrGetValue): IrExpression { + if (expression.descriptor == functionDescriptor.extensionReceiverParameter) + return newExtensionReceiver!! if (!variables.containsKey(expression.descriptor)) return expression return irGet(variables[expression.descriptor] as VariableDescriptor) @@ -114,12 +128,7 @@ class DefaultParameterStubGenerator internal constructor(val context: Context): dispatchReceiver = irThis() } if (functionDescriptor.extensionReceiverParameter != null) { - extensionReceiver = IrGetValueImpl( - startOffset = irFunction.startOffset, - endOffset = irFunction.endOffset, - descriptor = functionDescriptor.extensionReceiverParameter!!, - origin = null - ) + extensionReceiver = newExtensionReceiver } params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt new file mode 100644 index 00000000000..fad21920b5f --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt @@ -0,0 +1,141 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.ClassLoweringPass +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.utils.addToStdlib.singletonList + +internal class InnerClassLowering(val context: Context) : ClassLoweringPass { + override fun lower(irClass: IrClass) { + InnerClassTransformer(irClass).lowerInnerClass() + } + + private inner class InnerClassTransformer(val irClass: IrClass) { + val classDescriptor = irClass.descriptor + + lateinit var outerThisFieldDescriptor: PropertyDescriptor + + fun lowerInnerClass() { + if (!irClass.descriptor.isInner) return + + createOuterThisField() + lowerOuterThisReferences() + lowerConstructors() + } + + object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS : + IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS") + + private fun createOuterThisField() { + outerThisFieldDescriptor = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(classDescriptor) + + irClass.declarations.add(IrFieldImpl( + irClass.startOffset, irClass.endOffset, + DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS, + outerThisFieldDescriptor + )) + } + + private fun lowerConstructors() { + irClass.declarations.transformFlat { irMember -> + if (irMember is IrConstructor) + lowerConstructor(irMember).singletonList() + else + null + } + } + + private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor { + val descriptor = irConstructor.descriptor + val startOffset = irConstructor.startOffset + val endOffset = irConstructor.endOffset + + val dispatchReceiver = descriptor.dispatchReceiverParameter!! + + val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}") + + val instanceInitializerIndex = blockBody.statements.indexOfFirst { it is IrInstanceInitializerCall } + if (instanceInitializerIndex >= 0) { + // Initializing constructor: initialize 'this.this$0' with '$outer'. + blockBody.statements.add( + instanceInitializerIndex, + IrSetFieldImpl( + startOffset, endOffset, outerThisFieldDescriptor, + IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter), + IrGetValueImpl(startOffset, endOffset, dispatchReceiver) + ) + ) + } + else { + // Delegating constructor: invoke old constructor with dispatch receiver '$outer'. + val delegatingConstructorCall = (blockBody.statements.find { it is IrDelegatingConstructorCall } ?: + throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}") + ) as IrDelegatingConstructorCall + delegatingConstructorCall.dispatchReceiver = IrGetValueImpl( + delegatingConstructorCall.startOffset, delegatingConstructorCall.endOffset, dispatchReceiver + ) + } + + return irConstructor + } + + private fun lowerOuterThisReferences() { + irClass.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + + val implicitThisClass = expression.descriptor.getClassDescriptorForImplicitThis() ?: + return expression + + if (implicitThisClass == classDescriptor) return expression + + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val origin = expression.origin + + var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter, origin) + var innerClass = classDescriptor + + while (innerClass != implicitThisClass) { + if (!innerClass.isInner) { + // Captured 'this' unrelated to inner classes nesting hierarchy, leave it as is - + // should be transformed by closures conversion. + return expression + } + + val outerThisField = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(innerClass) + irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField, irThis, origin) + + val outer = classDescriptor.containingDeclaration + innerClass = outer as? ClassDescriptor ?: + throw AssertionError("Unexpected containing declaration for inner class $innerClass: $outer") + } + + return irThis + } + }) + } + + private fun ValueDescriptor.getClassDescriptorForImplicitThis(): ClassDescriptor? { + if (this is ReceiverParameterDescriptor) { + val receiverValue = value + if (receiverValue is ImplicitClassReceiver) { + return receiverValue.classDescriptor + } + } + return null + } + } +} + diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 3e9852323b6..71ef4b7d02d 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -342,6 +342,26 @@ task enum_companionObject(type: RunKonanTest) { source = "codegen/enum/companionObject.kt" } +task innerClass_simple(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/innerClass/simple.kt" +} + +task innerClass_getOuterVal(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/innerClass/getOuterVal.kt" +} + +task innerClass_generic(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/innerClass/generic.kt" +} + +task innerClass_qualifiedThis(type: RunKonanTest) { + goldValue = "OK\n" + source = "codegen/innerClass/qualifiedThis.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/innerClass/generic.kt b/backend.native/tests/codegen/innerClass/generic.kt new file mode 100644 index 00000000000..776d631f727 --- /dev/null +++ b/backend.native/tests/codegen/innerClass/generic.kt @@ -0,0 +1,15 @@ +class Outer { + inner class Inner(val t: T) { + fun box() = t + } +} + +fun box(): String { + if (Outer().Inner("OK").box() != "OK") return "Fail" + val x: Outer.Inner = Outer().Inner("OK") + return x.box() +} + +fun main(args: Array) { + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/innerClass/getOuterVal.kt b/backend.native/tests/codegen/innerClass/getOuterVal.kt new file mode 100644 index 00000000000..eb0c13b1de6 --- /dev/null +++ b/backend.native/tests/codegen/innerClass/getOuterVal.kt @@ -0,0 +1,12 @@ +class Outer(val s: String) { + inner class Inner { + fun box() = s + } +} + +fun box() = Outer("OK").Inner().box() + +fun main(args: Array) +{ + println(box()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/innerClass/qualifiedThis.kt b/backend.native/tests/codegen/innerClass/qualifiedThis.kt new file mode 100644 index 00000000000..3c7b395333e --- /dev/null +++ b/backend.native/tests/codegen/innerClass/qualifiedThis.kt @@ -0,0 +1,46 @@ +open class ABase +{ + open fun zzz() = "a_base" +} + +open class BBase +{ + open fun zzz() = "b_base" +} + +class D() { + val z = "d" +} + +class A: ABase() { // implicit label @A + val z = "a" + override fun zzz() = "a" + inner class B: BBase() { // implicit label @B + val z = "b" + override fun zzz() = "b" + fun D.foo() : String { // implicit label @foo + if(this@A.z != "a") return "Fail1" + if(this@B.z != "b") return "Fail2" + + if(super@A.zzz() != "a_base") return "Fail3" + if(super.zzz() != "b_base") return "Fail4" + if(super@B.zzz() != "b_base") return "Fail5" + if(this@A.zzz() != "a") return "Fail6" + if(this@B.zzz() != "b") return "Fail7" + + if(this.z != "d") return "Fail8" + + return "OK" + } + + fun bar(d: D): String { + return d.foo() + } + } +} + +fun box() = A().B().bar(D()) + +fun main(args : Array) { + println(box()) +} diff --git a/backend.native/tests/codegen/innerClass/simple.kt b/backend.native/tests/codegen/innerClass/simple.kt new file mode 100644 index 00000000000..be91e5c99d3 --- /dev/null +++ b/backend.native/tests/codegen/innerClass/simple.kt @@ -0,0 +1,12 @@ +class Outer { + inner class Inner { + fun box() = "OK" + } +} + +fun box() = Outer().Inner().box() + +fun main(args: Array) +{ + println(box()) +} \ No newline at end of file diff --git a/cmd/konanc b/cmd/konanc index b17e994bc25..ecc23b021be 100755 --- a/cmd/konanc +++ b/cmd/konanc @@ -56,7 +56,7 @@ INTEROP_JAR="${KONAN_HOME}/konan/lib/Runtime.jar" NATIVE_LIB="${KONAN_HOME}/konan/nativelib" KONAN_CLASSPATH="$KOTLIN_JAR:$INTEROP_JAR:$KONAN_JAR" KONAN_COMPILER=org.jetbrains.kotlin.cli.bc.K2NativeKt - +JAVA_OPTS=-ea # # KONAN BACKEND INVOCATION