JVM_IR: implement single-abstract-method conversions

This commit is contained in:
pyos
2019-03-26 12:19:51 +01:00
committed by max-kammerer
parent 141e8d553e
commit b74586f84e
77 changed files with 178 additions and 84 deletions
@@ -52,6 +52,12 @@ inline fun buildField(b: IrFieldBuilder.() -> Unit) =
buildField()
}
inline fun IrDeclarationContainer.addField(b: IrFieldBuilder.() -> Unit) =
buildField(b).also { field ->
field.parent = this
declarations.add(field)
}
fun IrPropertyBuilder.buildProperty(): IrProperty {
val wrappedDescriptor = WrappedPropertyDescriptor()
return IrPropertyImpl(
@@ -34,6 +34,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS :
IrDeclarationOriginImpl("SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS", isSynthetic = true)
object GENERATED_PROPERTY_REFERENCE : IrDeclarationOriginImpl("GENERATED_PROPERTY_REFERENCE", isSynthetic = true)
object GENERATED_SAM_IMPLEMENTATION : IrDeclarationOriginImpl("GENERATED_SAM_IMPLEMENTATION", isSynthetic = true)
}
interface JvmLoweredStatementOrigin : IrStatementOrigin {
@@ -82,6 +82,7 @@ val jvmPhases = namedIrFilePhase(
makePatchParentsPhase(1) then
jvmLocalDeclarationsPhase then
singleAbstractMethodPhase then
callableReferencePhase then
functionNVarargInvokePhase then
@@ -0,0 +1,156 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.serialization.fqNameSafe
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.sam.SamConstructorDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.OperatorNameConventions
internal val singleAbstractMethodPhase = makeIrFilePhase(
::SingleAbstractMethodLowering,
name = "SingleAbstractMethod",
description = "Replace SAM conversions with instances of interface-implementing classes"
)
class SingleAbstractMethodLowering(val context: CommonBackendContext) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
// TODO use `IrType`s; unfortunately, they are currently not comparable.
// (Can't use class symbols here, we want type parameters as well.)
val implementations = mutableMapOf<Pair<KotlinType, KotlinType>, IrClass>()
irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitCall(expression: IrCall): IrExpression {
if (expression.symbol.owner.descriptor !is SamConstructorDescriptor)
return super.visitCall(expression)
// Direct construction of an interface:
// Runnable { print("Hello, World!") }
return createInstance(expression.type, expression.getValueArgument(0)!!.transform(this, null))
}
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
if (expression.operator != IrTypeOperator.SAM_CONVERSION)
return super.visitTypeOperator(expression)
// Implicit conversion to an interface:
// val a: (Runnable) -> Unit = { it.run() } // pretend this is a Java method
// val b: () -> Unit = { print("Hello, World!") }
// a(b)
return createInstance(expression.typeOperand, expression.argument.transform(this, null))
}
// Replace a SAM-conversion of a callable object with an instance of an appropriate implementation.
private fun createInstance(superType: IrType, invokable: IrExpression): IrExpression {
val implementation = implementations.getOrPut(superType.toKotlinType() to invokable.type.toKotlinType()) {
createClass(superType, invokable.type)
}
// Running in the class context, so empty scope means this is a field/anonymous initializer.
val scopeOwnerSymbol = currentScope?.scope?.scopeOwnerSymbol ?: irClass.symbol
return context.createIrBuilder(scopeOwnerSymbol).irBlock(invokable, null, superType) {
// TODO if invokable is a callable reference, avoid materializing it?
if (superType.isNullable() && invokable.type.isNullable()) {
val invokableVariable = scope.createTemporaryVariable(invokable)
val instance = irCall(implementation.constructors.single()).apply { putValueArgument(0, irGet(invokableVariable)) }
+invokableVariable
+irIfNull(superType, irGet(invokableVariable), irNull(), instance)
} else {
+irCall(implementation.constructors.single()).apply { putValueArgument(0, invokable) }
}
}
}
// Construct a class that wraps an invokable object into an implementation of an interface:
// class sam$n(private val invokable: F) : Interface() { override fun method(...) = invokable(...) }
private fun createClass(superType: IrType, invokableType: IrType): IrClass {
val invokableClass = invokableType.classifierOrFail.owner as IrClass
val invoke = invokableClass.functions.single { it.name == OperatorNameConventions.INVOKE }
val superClass = superType.classifierOrFail.owner as IrClass
val superConstructor = if (superClass.kind != ClassKind.INTERFACE)
superClass.constructors.single { it.valueParameters.size == 0 }
else
context.irBuiltIns.anyClass.owner.constructors.single { it.valueParameters.size == 0 }
val superMethod = superClass.functions.single { it.modality == Modality.ABSTRACT }
val superClassName = superClass.fqNameSafe.pathSegments().joinToString("_") { it.toString() }
val subclass = buildClass {
// TODO this is not the name some tests (e.g. kt11519) expect
name = Name.identifier("sam\$${superClassName}\$${implementations.size}")
origin = JvmLoweredDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION
}.apply {
createImplicitParameterDeclarationWithWrappedDescriptor()
parent = irClass
// TODO convert all type parameters to upper bounds? See the kt11696 test.
superTypes += superType
}
val invokableField = subclass.addField {
name = Name.identifier("invokable")
type = invokableType
origin = subclass.origin
visibility = Visibilities.PRIVATE
}
subclass.addConstructor {
returnType = subclass.defaultType
origin = subclass.origin
isPrimary = true
}.apply {
addValueParameter {
name = invokableField.name
type = invokableType
index = 0
origin = subclass.origin
}
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
+irDelegatingConstructorCall(superConstructor)
+irSetField(irGet(subclass.thisReceiver!!), invokableField, irGet(valueParameters[0]))
+IrInstanceInitializerCallImpl(startOffset, endOffset, superClass.symbol, context.irBuiltIns.unitType)
}
}
subclass.addFunction {
name = superMethod.name
returnType = superMethod.returnType
visibility = superMethod.visibility
origin = subclass.origin
}.apply {
overriddenSymbols.add(superMethod.symbol)
dispatchReceiverParameter = subclass.thisReceiver!!.copyTo(this)
superMethod.valueParameters.mapTo(valueParameters) { it.copyTo(this) }
body = context.createIrBuilder(symbol).irBlockBody(startOffset, endOffset) {
+irReturn(irCall(invoke.symbol).apply {
dispatchReceiver = irGetField(irGet(subclass.thisReceiver!!), invokableField)
valueParameters.forEachIndexed { i, parameter -> putValueArgument(i, irGet(parameter)) }
})
}
}
return subclass
}
})
irClass.declarations.addAll(implementations.values)
}
}
@@ -379,7 +379,7 @@ private fun StatementGenerator.pregenerateValueArguments(call: CallBuilder, reso
generateSamConversionForValueArgumentsIfRequired(call, resolvedCall.resultingDescriptor)
}
private fun StatementGenerator.generateSamConversionForValueArgumentsIfRequired(call: CallBuilder, originalDescriptor: CallableDescriptor) {
fun StatementGenerator.generateSamConversionForValueArgumentsIfRequired(call: CallBuilder, originalDescriptor: CallableDescriptor) {
val underlyingDescriptor = context.extensions.samConversion.getOriginalForSamAdapter(originalDescriptor) ?: return
val originalValueParameters = originalDescriptor.valueParameters
@@ -72,6 +72,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
opCall.irValueArgumentsByIndex[0] = ktRight.genExpr()
statementGenerator.generateSamConversionForValueArgumentsIfRequired(opCall, opResolvedCall.resultingDescriptor)
val irOpCall = CallGenerator(statementGenerator).generateCall(ktExpression, opCall, origin)
if (isSimpleAssignment) {
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.inlineStatement
import org.jetbrains.kotlin.ir.expressions.isAssignmentOperatorWithResult
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
import org.jetbrains.kotlin.psi2ir.generators.generateSamConversionForValueArgumentsIfRequired
import org.jetbrains.kotlin.psi2ir.generators.pregenerateValueArgumentsUsing
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
@@ -98,8 +99,8 @@ class ArrayAccessAssignmentReceiver(
return LValueWithGetterAndSetterCalls(
callGenerator,
descriptor,
{ indexedGetCall()?.fillArrayAndIndexArguments(irArrayValue, indexedGetResolvedCall!!, ktExpressionToIrIndexValue) },
{ indexedSetCall()?.fillArrayAndIndexArguments(irArrayValue, indexedSetResolvedCall!!, ktExpressionToIrIndexValue) },
{ indexedGetCall()?.fillArguments(irArrayValue, indexedGetResolvedCall!!, ktExpressionToIrIndexValue, null) },
{ indexedSetCall()?.fillArguments(irArrayValue, indexedSetResolvedCall!!, ktExpressionToIrIndexValue, it) },
callGenerator.translateType(kotlinType),
startOffset, endOffset, origin
)
@@ -107,24 +108,22 @@ class ArrayAccessAssignmentReceiver(
override fun assign(value: IrExpression): IrExpression {
val call = indexedSetCall() ?: throw AssertionError("Array access without indexed-get call")
val ktExpressionToIrIndexExpression = ktIndexExpressions.zip(irIndexExpressions).toMap()
call.setExplicitReceiverValue(OnceExpressionValue(irArray))
callGenerator.statementGenerator.pregenerateValueArgumentsUsing(call, indexedSetResolvedCall!!) {
ktExpressionToIrIndexExpression[it]
}
call.lastArgument = value
val ktExpressionToIrIndexExpression = ktIndexExpressions.zip(irIndexExpressions.map { OnceExpressionValue(it) }).toMap()
call.fillArguments(OnceExpressionValue(irArray), indexedSetResolvedCall!!, ktExpressionToIrIndexExpression, value)
return callGenerator.generateCall(startOffset, endOffset, call, IrStatementOrigin.EQ)
}
private fun CallBuilder.fillArrayAndIndexArguments(
private fun CallBuilder.fillArguments(
arrayValue: IntermediateValue,
resolvedCall: ResolvedCall<FunctionDescriptor>,
ktExpressionToIrIndexValue: Map<KtExpression, IntermediateValue>
ktExpressionToIrIndexValue: Map<KtExpression, IntermediateValue>,
value: IrExpression?
) = apply {
setExplicitReceiverValue(arrayValue)
callGenerator.statementGenerator.pregenerateValueArgumentsUsing(this, resolvedCall) { ktExpression ->
ktExpressionToIrIndexValue[ktExpression]?.load()
}
value?.let { lastArgument = it }
callGenerator.statementGenerator.generateSamConversionForValueArgumentsIfRequired(this, resolvedCall.resultingDescriptor)
}
}
@@ -26,7 +26,7 @@ class LValueWithGetterAndSetterCalls(
val callGenerator: CallGenerator,
val descriptor: CallableDescriptor,
val getterCall: () -> CallBuilder?,
val setterCall: () -> CallBuilder?,
val setterCall: (IrExpression) -> CallBuilder?,
override val type: IrType,
val startOffset: Int,
val endOffset: Int,
@@ -39,8 +39,7 @@ class LValueWithGetterAndSetterCalls(
}
override fun store(irExpression: IrExpression): IrExpression {
val call = setterCall() ?: throw AssertionError("No setter call for $descriptor")
call.irValueArgumentsByIndex[call.argumentsCount - 1] = irExpression
val call = setterCall(irExpression) ?: throw AssertionError("No setter call for $descriptor")
return callGenerator.generateCall(startOffset, endOffset, call, origin)
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: F.java
import java.util.function.Function;
-1
View File
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
-1
View File
@@ -1,5 +1,4 @@
// JVM_TARGET: 1.8
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FULL_JDK
// WITH_RUNTIME
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: JavaCall.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
import java.io.*
@@ -1,5 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: JVM_IR
// SKIP_JDK6
// FULL_JDK
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FULL_JDK
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FULL_JDK
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
import java.io.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
var result = "FAIL"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
var result = "FAIL"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
class Box(val s: String) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: JavaClass.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: MFunction.java
public interface MFunction<T, R> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
// KT-5912
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaInterface.java
interface JavaInterface {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: GenericInterface.java
interface GenericInterface<T> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import java.io.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import java.util.Arrays;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: Super.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: Sub.java
class Super {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import org.jetbrains.annotations.NotNull;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,5 +1,4 @@
// !LANGUAGE: -ProhibitOperatorMod
// IGNORE_BACKEND: JVM_IR
// FILE: Java.java
import org.jetbrains.annotations.NotNull;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass<T> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: test.kt
class Test : Base {
constructor(f: () -> String) : super(f)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: JavaClass.java
class JavaClass {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: WeirdComparator.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: WeirdComparator.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: WeirdComparator.java
import java.util.*;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: Custom.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: Base.java
interface Base<T> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: ParamBase.java
class ParamBase<T> {}
@@ -1,5 +1,4 @@
// SKIP_JDK6
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// FILE: JavaClass.java
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: test/Foo.java
package test;
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// FILE: 1.kt
// FULL_JDK
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// SKIP_JDK6
// FILE: A.kt
import java.util.*