JVM_IR indy-SAM conversions, 1st passing tests

KT-44278 KT-26060 KT-42621
This commit is contained in:
Dmitry Petrov
2021-01-14 17:08:17 +03:00
parent c13949d322
commit 3140cca050
37 changed files with 1144 additions and 58 deletions
@@ -207,6 +207,14 @@ class GenerationState private constructor(
configuration.get(JVMConfigurationKeys.STRING_CONCAT) ?: JvmStringConcat.INLINE
else JvmStringConcat.INLINE
val samConversionsScheme = run {
val fromConfig = configuration.get(JVMConfigurationKeys.SAM_CONVERSIONS) ?: JvmSamConversions.DEFAULT
if (target >= fromConfig.minJvmTarget)
fromConfig
else
JvmSamConversions.DEFAULT
}
val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module)
val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode
val bindingTrace: BindingTrace = DelegatingBindingTrace(
@@ -362,6 +362,15 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var stringConcat: String? by NullableStringFreezableVar(JvmStringConcat.INLINE.description)
@Argument(
value = "-Xsam-conversions",
valueDescription = "{class|indy}",
description = """Select code generation scheme for SAM conversions.
-Xsam-conversions=indy Generate SAM conversions using `invokedynamic` with `LambdaMetafactory.metafactory`. Requires `-jvm-target 8` or greater.
-Xsam-conversions=class Generate SAM conversions as explicit classes"""
)
var samConversions: String? by NullableStringFreezableVar(JvmSamConversions.CLASS.description)
@Argument(
value = "-Xklib",
valueDescription = "<path>",
@@ -59,8 +59,28 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
}
} else {
messageCollector.report(
ERROR, "Unknown -Xstring-concat mode: ${arguments.jvmTarget}\n" +
"Supported versions: ${JvmStringConcat.values().joinToString { it.description }}"
ERROR, "Unknown `-Xstring-concat` mode: ${arguments.stringConcat}\n" +
"Supported modes: ${JvmStringConcat.values().joinToString { it.description }}"
)
}
}
if (arguments.samConversions != null) {
val samConversions = JvmSamConversions.fromString(arguments.samConversions)
if (samConversions != null) {
put(JVMConfigurationKeys.SAM_CONVERSIONS, samConversions)
if (jvmTarget < samConversions.minJvmTarget) {
messageCollector.report(
WARNING,
"`-Xsam-conversions=${arguments.samConversions}` requires JVM target at least " +
"${samConversions.minJvmTarget.description} and is ignored."
)
}
} else {
messageCollector.report(
ERROR,
"Unknown `-Xsam-conversions` argument: ${arguments.samConversions}\n" +
"Supported arguments: ${JvmSamConversions.values().joinToString { it.description }}"
)
}
}
@@ -114,6 +114,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<JvmStringConcat> STRING_CONCAT =
CompilerConfigurationKey.create("Specifies string concatenation scheme");
public static final CompilerConfigurationKey<JvmSamConversions> SAM_CONVERSIONS =
CompilerConfigurationKey.create("SAM conversions code generation scheme");
public static final CompilerConfigurationKey<List<String>> KLIB_PATHS =
CompilerConfigurationKey.create("Paths to .klib libraries");
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2021 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 JvmSamConversions(
val description: String,
val minJvmTarget: JvmTarget
) {
CLASS("class", JvmTarget.JVM_1_6),
INDY("indy", JvmTarget.JVM_1_8),
;
companion object {
val DEFAULT = CLASS
@JvmStatic
fun fromString(string: String?) =
values().find { it.description == string }
}
}
@@ -18379,6 +18379,74 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
public class Invokedynamic extends AbstractFirBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
public class Sam extends AbstractFirBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("boundReference.kt")
public void testBoundReference() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/boundReference.kt");
}
@Test
@TestMetadata("capturedDispatchReceiver.kt")
public void testCapturedDispatchReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedDispatchReceiver.kt");
}
@Test
@TestMetadata("capturedExtensionReceiver.kt")
public void testCapturedExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedExtensionReceiver.kt");
}
@Test
@TestMetadata("capturingIndyFunInterface.kt")
public void testCapturingIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndyFunInterface.kt");
}
@Test
@TestMetadata("capturingIndySam.kt")
public void testCapturingIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndySam.kt");
}
@Test
@TestMetadata("primitiveVsWrapperInSam.kt")
public void testPrimitiveVsWrapperInSam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("simpleIndyFunInterface.kt")
public void testSimpleIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndyFunInterface.kt");
}
@Test
@TestMetadata("simpleIndySam.kt")
public void testSimpleIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
}
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@@ -65,7 +65,11 @@ fun IrClass.addField(fieldName: Name, fieldType: IrType, fieldVisibility: Descri
visibility = fieldVisibility
}
fun IrClass.addField(fieldName: String, fieldType: IrType, fieldVisibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE): IrField =
fun IrClass.addField(
fieldName: String,
fieldType: IrType,
fieldVisibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE
): IrField =
addField(Name.identifier(fieldName), fieldType, fieldVisibility)
@PublishedApi
@@ -141,16 +145,16 @@ inline fun IrClass.addFunction(builder: IrFunctionBuilder.() -> Unit): IrSimpleF
factory.addFunction(this, builder)
fun IrClass.addFunction(
name: String,
returnType: IrType,
modality: Modality = Modality.FINAL,
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
isStatic: Boolean = false,
isSuspend: Boolean = false,
isFakeOverride: Boolean = false,
origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
name: String,
returnType: IrType,
modality: Modality = Modality.FINAL,
visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
isStatic: Boolean = false,
isSuspend: Boolean = false,
isFakeOverride: Boolean = false,
origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED,
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
): IrSimpleFunction =
addFunction {
this.startOffset = startOffset
@@ -216,7 +220,7 @@ internal fun IrFactory.buildValueParameter(builder: IrValueParameterBuilder, par
inline fun <D> buildValueParameter(declaration: D, builder: IrValueParameterBuilder.() -> Unit): IrValueParameter
where D : IrDeclaration, D : IrDeclarationParent =
where D : IrDeclaration, D : IrDeclarationParent =
IrValueParameterBuilder().run {
builder()
declaration.factory.buildValueParameter(this, declaration)
@@ -234,8 +238,11 @@ inline fun IrFunction.addValueParameter(builder: IrValueParameterBuilder.() -> U
}
fun IrFunction.addValueParameter(name: String, type: IrType, origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED): IrValueParameter =
addValueParameter(Name.identifier(name), type, origin)
fun IrFunction.addValueParameter(name: Name, type: IrType, origin: IrDeclarationOrigin = IrDeclarationOrigin.DEFINED): IrValueParameter =
addValueParameter {
this.name = Name.identifier(name)
this.name = name
this.type = type
this.origin = origin
}
@@ -46,4 +46,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_PROPERTY_BACKING_FIELD")
object FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE")
object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB")
object INVOVEDYNAMIC_CALL_TARGET : IrDeclarationOriginImpl("INVOVEDYNAMIC_CALL_TARGET")
}
@@ -8,7 +8,6 @@
package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.ir.addChild
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.builtins.StandardNames
@@ -51,6 +50,17 @@ class JvmSymbols(
private val kotlinReflectPackage: IrPackageFragment = createPackage(FqName("kotlin.reflect"))
private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang"))
// Special package for functions representing dynamic symbols referenced by 'INVOKEDYNAMIC' instruction - e.g.,
// 'get(Ljava/lang/String;)Ljava/util/function/Supplier;'
// in
// INVOKEDYNAMIC get(Ljava/lang/String;)Ljava/util/function/Supplier; [
// H_INVOKESTATIC java/lang/invoke/LambdaMetafactory.metafactory(...)Ljava/lang/invoke/CallSite;
// ...
// ]
// Such functions don't exist as methods in the actual bytecode
// (they are expected to be provided at run-time by the corresponding bootstrap method).
val kotlinJvmInternalInvokeDynamicPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.internal.invokeDynamic"))
private val generateOptimizedCallableReferenceSuperClasses = context.state.generateOptimizedCallableReferenceSuperClasses
private fun createPackage(fqName: FqName): IrPackageFragment =
@@ -535,6 +545,82 @@ class JvmSymbols(
returnType = dst.defaultType
}.symbol
val indySamConversionIntrinsic: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<jvm-indy-sam-conversion>")
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
}.apply {
parent = kotlinJvmInternalPackage
val samType = addTypeParameter("SAM_TYPE", irBuiltIns.anyType)
addValueParameter("method", irBuiltIns.anyNType)
returnType = samType.defaultType
}.symbol
val arrayOfAnyType = irBuiltIns.arrayClass.typeWith(irBuiltIns.anyType)
// Intrinsic to represent INVOKEDYNAMIC calls in IR.
// fun <T> `<jvm-indy>`(
// dynamicCall: T,
// bootstrapMethodTag: Int,
// bootstrapMethodOwner: String,
// bootstrapMethodName: String,
// bootstrapMethodDesc: String,
// vararg bootstrapMethodArgs: Any
// ): T
// Bootstrap method handle is encoded as a bunch of constants.
// For example,
// REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(
// Ljava/lang/invoke/MethodHandles$Lookup;
// Ljava/lang/String;
// Ljava/lang/invoke/MethodType;
// Ljava/lang/invoke/MethodType;
// Ljava/lang/invoke/MethodHandle;
// Ljava/lang/invoke/MethodType;
// )Ljava/lang/invoke/CallSite;
// is represented as
// bootstrapMethodTag: IrConst<Int>(value = H_INVOKESTATIC)
// bootstrapMethodOwner: IrConst<String>(value = "java/lang/invoke/LambdaMetafactory")
// bootstrapMethodName: IrConst<String>(value = "metafactory")
// bootstrapMethodDesc: IrConst<String>(value = "(...)Ljava/lang/invoke/CallSite;")
// Bootstrap method owner is assumed to be a class (which is true for both LambdaMetafactory and StringConcatFactory).
val jvmIndyIntrinsic: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<jvm-indy>")
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
}.apply {
parent = kotlinJvmInternalPackage
val t = addTypeParameter("T", irBuiltIns.anyNType)
addValueParameter("dynamicCall", t.defaultType)
addValueParameter("bootstrapMethodTag", irBuiltIns.intType)
addValueParameter("bootstrapMethodOwner", irBuiltIns.stringType)
addValueParameter("bootstrapMethodName", irBuiltIns.stringType)
addValueParameter("bootstrapMethodDesc", irBuiltIns.stringType)
addValueParameter {
name = Name.identifier("bootstrapMethodArguments")
type = arrayOfAnyType
varargElementType = irBuiltIns.anyType
}
returnType = t.defaultType
}.symbol
// Intrinsic used to represent MethodType objects in bootstrap method arguments (see jvmInvokeDynamicIntrinsic above).
// Type argument is a possibly substituted method owner type (e.g., 'java.lang.function.Supplier<String>').
// Value argument is a raw function reference to a corresponding method (e.g., 'java.lang.function.Supplier#get').
val jvmMethodTypeIntrinsic: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<jvm-method-type>")
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
}.apply {
parent = kotlinJvmInternalPackage
addTypeParameter("OwnerT", irBuiltIns.anyType)
addValueParameter("method", irBuiltIns.anyType)
returnType = irBuiltIns.anyType
}.symbol
val flexibleNullabilityAnnotationFqName = JvmGeneratorExtensions.FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME
val enhancedNullabilityAnnotationFqName = JvmGeneratorExtensions.ENHANCED_NULLABILITY_ANNOTATION_FQ_NAME
val rawTypeAnnotationFQN = JvmGeneratorExtensions.RAW_TYPE_ANNOTATION_FQ_NAME
private val collectionToArrayClass: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.CollectionToArray")) { klass ->
klass.origin = JvmLoweredDeclarationOrigin.TO_ARRAY
@@ -418,7 +418,7 @@ class ExpressionCodegen(
val callee = expression.symbol.owner
require(callee.parent is IrClass) { "Unhandled intrinsic in ExpressionCodegen: ${callee.render()}" }
val callable = methodSignatureMapper.mapToCallableMethod(irFunction, expression)
val callable = methodSignatureMapper.mapToCallableMethod(expression, irFunction)
val callGenerator = getOrCreateCallGenerator(expression, data, callable.signature)
val isSuspensionPoint = expression.isSuspensionPoint()
@@ -366,7 +366,8 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
if (valueArgumentsCount > 0) (getValueArgument(0) as? IrConst<*>)?.value as? Boolean ?: true else null
}
fun mapToCallableMethod(caller: IrFunction, expression: IrCall): IrCallableMethod {
// TODO get rid of 'caller' argument
internal fun mapToCallableMethod(expression: IrCall, caller: IrFunction?): IrCallableMethod {
val callee = expression.symbol.owner
val calleeParent = expression.superQualifierSymbol?.owner
?: expression.dispatchReceiver?.type?.classOrNull?.owner
@@ -385,17 +386,21 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
}
val declaration = findSuperDeclaration(callee, isSuperCall)
val signature = mapOverriddenSpecialBuiltinIfNeeded(caller, declaration, isSuperCall)
?: mapSignatureSkipGeneric(declaration)
val signature =
if (caller != null && caller.isBridge()) {
// Do not remap special builtin methods when called from a bridge. The bridges are there to provide the
// remapped name or signature and forward to the actually declared method.
mapSignatureSkipGeneric(declaration)
} else {
mapOverriddenSpecialBuiltinIfNeeded(declaration, isSuperCall)
?: mapSignatureSkipGeneric(declaration)
}
return IrCallableMethod(owner, invokeOpcode, signature, isInterface)
}
// TODO: get rid of this (probably via some special lowering)
private fun mapOverriddenSpecialBuiltinIfNeeded(caller: IrFunction, callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
// Do not remap special builtin methods when called from a bridge. The bridges are there to provide the
// remapped name or signature and forward to the actually declared method.
if (caller.isBridge()) return null
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
// Do not remap calls to static replacements of inline class methods, since they have completely different signatures.
if (callee.isStaticInlineClassReplacement) return null
val overriddenSpecialBuiltinFunction =
@@ -120,6 +120,7 @@ class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
symbols.throwTypeCastException.toKey()!! to ThrowException(Type.getObjectType("kotlin/TypeCastException")),
symbols.throwUnsupportedOperationException.toKey()!! to ThrowException(Type.getObjectType("java/lang/UnsupportedOperationException")),
symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException,
symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic
) +
numberConversionMethods() +
unaryFunForPrimitives("plus", UnaryPlus) +
@@ -0,0 +1,133 @@
/*
* Copyright 2010-2021 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.backend.jvm.intrinsics
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.*
import org.jetbrains.kotlin.codegen.inline.v
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Handle
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
object JvmInvokeDynamic : IntrinsicMethod() {
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
fun fail(message: String): Nothing =
throw AssertionError("$message; expression:\n${expression.dump()}")
val dynamicCall = expression.getValueArgument(0) as? IrCall
?: fail("'dynamicCall' is expected to be a call")
val dynamicCallee = dynamicCall.symbol.owner
if (dynamicCallee.parent != codegen.context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage ||
dynamicCallee.origin != JvmLoweredDeclarationOrigin.INVOVEDYNAMIC_CALL_TARGET
)
fail("Unexpected dynamicCallee: '${dynamicCallee.render()}'")
val bootstrapMethodTag = expression.getValueArgument(1)?.getIntConst()
?: fail("'bootstrapMethodTag' is expected to be an int const")
val bootstrapMethodOwner = expression.getValueArgument(2)?.getStringConst()
?: fail("'bootstrapMethodOwner' is expected to be a string const")
val bootstrapMethodName = expression.getValueArgument(3)?.getStringConst()
?: fail("'bootstrapMethodName' is expected to be a string const")
val bootstrapMethodDesc = expression.getValueArgument(4)?.getStringConst()
?: fail("'bootstrapMethodDesc' is expected to be a string const")
val bootstrapMethodArgs = (expression.getValueArgument(5)?.safeAs<IrVararg>()
?: fail("'bootstrapMethodArgs' is expected to be a vararg"))
val asmBootstrapMethodArgs = bootstrapMethodArgs.elements
.map { generateBootstrapMethodArg(it, codegen) }
.toTypedArray()
val dynamicCalleeMethod = codegen.methodSignatureMapper.mapAsmMethod(dynamicCallee)
val bootstrapMethodHandle = Handle(bootstrapMethodTag, bootstrapMethodOwner, bootstrapMethodName, bootstrapMethodDesc, false)
val dynamicCallGenerator = IrCallGenerator.DefaultCallGenerator
val dynamicCalleeArgumentTypes = dynamicCalleeMethod.argumentTypes
for (i in dynamicCallee.valueParameters.indices) {
val dynamicCalleeParameter = dynamicCallee.valueParameters[i]
val dynamicCalleeArgument = dynamicCall.getValueArgument(i)
?: fail("No argument #$i in 'dynamicCall'")
val dynamicCalleeArgumentType = dynamicCalleeArgumentTypes.getOrElse(i) {
fail("No argument type #$i in dynamic callee: $dynamicCalleeMethod")
}
dynamicCallGenerator.genValueAndPut(dynamicCalleeParameter, dynamicCalleeArgument, dynamicCalleeArgumentType, codegen, data)
}
codegen.v.invokedynamic(dynamicCalleeMethod.name, dynamicCalleeMethod.descriptor, bootstrapMethodHandle, asmBootstrapMethodArgs)
return MaterialValue(codegen, dynamicCalleeMethod.returnType, expression.type)
}
private fun generateBootstrapMethodArg(element: IrVarargElement, codegen: ExpressionCodegen): Any =
when (element) {
is IrRawFunctionReference ->
generateMethodHandle(element, codegen)
is IrCall ->
when (element.symbol) {
codegen.context.ir.symbols.jvmMethodTypeIntrinsic ->
generateMethodType(element, codegen)
else ->
throw AssertionError("Unexpected callee in bootstrap method argument:\n${element.dump()}")
}
is IrConst<*> ->
when (element.kind) {
IrConstKind.Byte -> (element.value as Byte).toInt()
IrConstKind.Short -> (element.value as Short).toInt()
IrConstKind.Int -> element.value as Int
IrConstKind.Long -> element.value as Long
IrConstKind.Float -> element.value as Float
IrConstKind.Double -> element.value as Double
IrConstKind.String -> element.value as String
else ->
throw AssertionError("Unexpected constant expression in bootstrap method argument:\n${element.dump()}")
}
else ->
throw AssertionError("Unexpected bootstrap method argument:\n${element.dump()}")
}
private fun generateMethodHandle(irRawFunctionReference: IrRawFunctionReference, codegen: ExpressionCodegen): Any {
val irFun = irRawFunctionReference.symbol.owner
val irParentClass = irFun.parentAsClass
val owner = codegen.typeMapper.mapOwner(irParentClass)
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun)
val handleTag = when {
irFun.dispatchReceiverParameter == null ->
Opcodes.H_INVOKESTATIC
else ->
Opcodes.H_INVOKEVIRTUAL
}
return Handle(handleTag, owner.internalName, asmMethod.name, asmMethod.descriptor, irParentClass.isJvmInterface)
}
private fun generateMethodType(jvmMethodTypeCall: IrCall, codegen: ExpressionCodegen): Any {
val irRawFunctionReference = jvmMethodTypeCall.getValueArgument(0) as? IrRawFunctionReference
?: throw AssertionError(
"Argument in ${jvmMethodTypeCall.symbol.owner.name} call is expected to be a raw function reference:\n" +
jvmMethodTypeCall.dump()
)
val irFun = irRawFunctionReference.symbol.owner
// TODO substitute signature
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun)
return Type.getMethodType(asmMethod.descriptor)
}
private fun IrExpression.getIntConst() =
if (this is IrConst<*> && kind == IrConstKind.Int)
this.value as Int
else
null
private fun IrExpression.getStringConst() =
if (this is IrConst<*> && kind == IrConstKind.String)
this.value as String
else
null
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.ir.*
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
import org.jetbrains.kotlin.config.JvmSamConversions
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -21,10 +22,7 @@ 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.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -80,6 +78,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
FunctionReferenceBuilder(expression).build()
}
private val shouldUseIndySamConversions =
context.state.samConversionsScheme == JvmSamConversions.INDY
// Handle SAM conversions which wrap a function reference:
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
//
@@ -87,20 +88,66 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
// This is actually very common, as `Interface { something }` is a local function + a SAM-conversion
// of a reference to it into an implementation.
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
if (expression.operator == IrTypeOperator.SAM_CONVERSION) {
val invokable = expression.argument
val reference = if (invokable is IrFunctionReference) {
invokable
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
invokable.statements.last() as IrFunctionReference
} else {
return super.visitTypeOperator(expression)
}
reference.transformChildrenVoid()
return FunctionReferenceBuilder(reference, expression.typeOperand).build()
if (expression.operator != IrTypeOperator.SAM_CONVERSION) {
return super.visitTypeOperator(expression)
}
val invokable = expression.argument
val reference = if (invokable is IrFunctionReference) {
invokable
} else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) {
invokable.statements.dropLast(1).forEach { it.transform(this, null) }
invokable.statements.last() as IrFunctionReference
} else {
return super.visitTypeOperator(expression)
}
reference.transformChildrenVoid()
return if (shouldUseIndySamConversions) {
wrapSamConversionArgumentWithIndySamConversion(expression)
} else {
FunctionReferenceBuilder(reference, expression.typeOperand).build()
}
}
private fun wrapSamConversionArgumentWithIndySamConversion(expression: IrTypeOperatorCall): IrExpression {
return when (val argument = expression.argument) {
is IrFunctionReference ->
wrapWithIndySamConversion(expression.typeOperand, argument)
is IrBlock -> {
val last = argument.statements.last()
val functionReference = last as? IrFunctionReference
?: throw AssertionError("Function reference expected: ${last.render()}")
argument.statements[argument.statements.size - 1] = wrapWithIndySamConversion(expression.typeOperand, functionReference)
return argument
}
else -> throw AssertionError("Block or function reference expected: ${expression.render()}")
}
}
private val jvmIndySamConversionIntrinsic = context.ir.symbols.indySamConversionIntrinsic
private val specialNullabilityAnnotationsFqNames =
setOf(
context.ir.symbols.flexibleNullabilityAnnotationFqName,
context.ir.symbols.enhancedNullabilityAnnotationFqName
)
private fun wrapWithIndySamConversion(samType: IrType, irFunRef: IrFunctionReference): IrCall {
val notNullSamType = samType.makeNotNull()
.removeAnnotations { it.type.classFqName in specialNullabilityAnnotationsFqNames }
return context.createJvmIrBuilder(currentScope!!.scope.scopeOwnerSymbol).run {
// We should produce the following expression:
// `<jvm-indy-sam-conversion>`<samType>(method)
// where:
// - 'samType' is a substituted SAM type;
// - 'method' is a function reference to the actual method we are going to call
// (note that we need an IrFunctionReference here, so that further transformations would extract closure properly).
irCall(jvmIndySamConversionIntrinsic, notNullSamType).apply {
putTypeArgument(0, notNullSamType)
putValueArgument(0, irFunRef)
}
}
return super.visitTypeOperator(expression)
}
private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) {
@@ -15,8 +15,6 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.util.isLambda
import org.jetbrains.kotlin.ir.visitors.*
internal val removeDeclarationsThatWouldBeInlined = makeIrModulePhase(
@@ -11,26 +11,33 @@ import org.jetbrains.kotlin.backend.common.lower.at
import org.jetbrains.kotlin.backend.common.lower.irNot
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.codegen.fileParent
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.isInlined
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Handle
import org.jetbrains.org.objectweb.asm.Opcodes
// After this pass runs there are only four kinds of IrTypeOperatorCalls left:
//
@@ -95,6 +102,175 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil
builder.irAs(argument, type)
}
private val indySamConversionIntrinsic = context.ir.symbols.indySamConversionIntrinsic
private val indyIntrinsic = context.ir.symbols.jvmIndyIntrinsic
private fun IrBuilderWithScope.jvmInvokeDynamic(
dynamicCall: IrCall,
bootstrapMethod: Handle,
bootstrapMethodArguments: List<IrExpression>
) =
irCall(indyIntrinsic, dynamicCall.type).apply {
putTypeArgument(0, dynamicCall.type)
putValueArgument(0, dynamicCall)
putValueArgument(1, irInt(bootstrapMethod.tag))
putValueArgument(2, irString(bootstrapMethod.owner))
putValueArgument(3, irString(bootstrapMethod.name))
putValueArgument(4, irString(bootstrapMethod.desc))
putValueArgument(5, irVararg(context.irBuiltIns.anyType, bootstrapMethodArguments))
}
private val methodTypeIntrinsic = context.ir.symbols.jvmMethodTypeIntrinsic
private fun IrBuilderWithScope.jvmMethodType(ownerType: IrType, methodSymbol: IrFunctionSymbol) =
irCall(methodTypeIntrinsic, context.irBuiltIns.anyType).apply {
putTypeArgument(0, ownerType)
putValueArgument(0, irRawFunctionReferefence(context.irBuiltIns.anyType, methodSymbol))
}
private val lambdaMetafactoryHandle =
Handle(
Opcodes.H_INVOKESTATIC,
"java/lang/invoke/LambdaMetafactory",
"metafactory",
"(" +
"Ljava/lang/invoke/MethodHandles\$Lookup;" +
"Ljava/lang/String;" +
"Ljava/lang/invoke/MethodType;" +
"Ljava/lang/invoke/MethodType;" +
"Ljava/lang/invoke/MethodHandle;" +
"Ljava/lang/invoke/MethodType;" +
")Ljava/lang/invoke/CallSite;",
false
)
override fun visitCall(expression: IrCall): IrExpression {
return when (expression.symbol) {
indySamConversionIntrinsic -> updateIndySamConversionIntrinsicCall(expression)
else -> super.visitCall(expression)
}
}
/**
* @see FunctionReferenceLowering.wrapWithIndySamConversion
*/
private fun updateIndySamConversionIntrinsicCall(call: IrCall): IrCall {
fun fail(message: String): Nothing =
throw AssertionError("$message, call:\n${call.dump()}")
// We expect:
// `<jvm-indy-sam-conversion>`<samType>(method)
// where
// - 'samType' is a substituted SAM type;
// - 'method' is an IrFunctionReference to an actual method that should be called,
// with arguments captured by closure stored as function reference arguments.
// We replace it with JVM INVOKEDYNAMIC intrinsic.
val startOffset = call.startOffset
val endOffset = call.endOffset
val samType = call.getTypeArgument(0) as? IrSimpleType
?: fail("'samType' is expected to be a simple type")
val samClassSymbol = samType.classOrNull
?: fail("'samType' is expected to be a class type: '${samType.render()}'")
val samMethod = samClassSymbol.owner.functions.singleOrNull { it.modality == Modality.ABSTRACT }
?: fail("'${samType.render()}' is not a SAM-type")
val irFunRef = call.getValueArgument(0) as? IrFunctionReference
?: fail("'method' is expected to be 'IrFunctionReference'")
val funSymbol = irFunRef.symbol
val erasedSamType = samClassSymbol.defaultType as IrSimpleType
val dynamicCall = wrapClosureInDynamicCall(erasedSamType, samMethod, irFunRef)
return context.createJvmIrBuilder(
funSymbol, // TODO actual symbol for outer scope
startOffset, endOffset
).run {
val samMethodType = jvmMethodType(erasedSamType, samMethod.symbol)
val irRawFunRef = irRawFunctionReferefence(irFunRef.type, funSymbol)
val instanceMethodType = jvmMethodType(samType, samMethod.symbol)
jvmInvokeDynamic(
dynamicCall,
lambdaMetafactoryHandle,
listOf(samMethodType, irRawFunRef, instanceMethodType)
)
}
}
private fun wrapClosureInDynamicCall(
erasedSamType: IrSimpleType,
samMethod: IrSimpleFunction,
irFunRef: IrFunctionReference
): IrCall {
fun fail(message: String): Nothing =
throw AssertionError("$message, irFunRef:\n${irFunRef.dump()}")
val dynamicCallArguments = ArrayList<IrExpression>()
val irDynamicCallTarget = context.irFactory.buildFun {
origin = JvmLoweredDeclarationOrigin.INVOVEDYNAMIC_CALL_TARGET
name = samMethod.name
returnType = erasedSamType
}.apply {
parent = context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage
var syntheticParameterIndex = 0
val targetFun = irFunRef.symbol.owner
val targetDispatchReceiverParameter = targetFun.dispatchReceiverParameter
if (targetDispatchReceiverParameter != null) {
addValueParameter("p${syntheticParameterIndex++}", targetDispatchReceiverParameter.type)
val dispatchReceiver = irFunRef.dispatchReceiver
?: fail("Captured dispatch receiver is not provided")
dynamicCallArguments.add(dispatchReceiver)
}
val targetExtensionReceiverParameter = targetFun.extensionReceiverParameter
if (targetExtensionReceiverParameter != null) {
addValueParameter("p${syntheticParameterIndex++}", targetExtensionReceiverParameter.type)
val extensionReceiver = irFunRef.extensionReceiver
?: fail("Captured extension receiver is not provided")
dynamicCallArguments.add(extensionReceiver)
}
val samMethodValueParametersCount = samMethod.valueParameters.size
val targetFunValueParametersCount = targetFun.valueParameters.size
for (i in 0 until targetFunValueParametersCount - samMethodValueParametersCount) {
val targetFunValueParameter = targetFun.valueParameters[i]
addValueParameter("p${syntheticParameterIndex++}", targetFunValueParameter.type)
val capturedValueArgument = irFunRef.getValueArgument(i)
?: fail("Captured value argument #$i (${targetFunValueParameter.name} not provided")
dynamicCallArguments.add(capturedValueArgument)
}
}
if (dynamicCallArguments.size != irDynamicCallTarget.valueParameters.size) {
throw AssertionError(
"Dynamic call target value parameters (${irDynamicCallTarget.valueParameters.size}) " +
"don't match dynamic call arguments (${dynamicCallArguments.size}):\n" +
"irDynamicCallTarget:\n" +
irDynamicCallTarget.dump() +
"dynamicCallArguments:\n" +
dynamicCallArguments
.withIndex()
.joinToString(separator = "\n ", prefix = "[\n ", postfix = "\n]") { (index, irArg) ->
"#$index: ${irArg.dump()}"
}
)
}
return context.createJvmIrBuilder(irDynamicCallTarget.symbol)
.irCall(irDynamicCallTarget.symbol)
.apply {
for (i in dynamicCallArguments.indices) {
putValueArgument(i, dynamicCallArguments[i])
}
}
}
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression = with(builder) {
at(expression)
return when (expression.operator) {
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.isImmutable
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
@@ -326,6 +327,11 @@ fun IrBuilderWithScope.irString(value: String) =
fun IrBuilderWithScope.irConcat() =
IrStringConcatenationImpl(startOffset, endOffset, context.irBuiltIns.stringType)
fun IrBuilderWithScope.irVararg(elementType: IrType, values: List<IrExpression>) =
IrVarargImpl(startOffset, endOffset, context.irBuiltIns.arrayClass.typeWith(elementType), elementType, values)
fun IrBuilderWithScope.irRawFunctionReferefence(type: IrType, symbol: IrFunctionSymbol) =
IrRawFunctionReferenceImpl(startOffset, endOffset, type, symbol)
inline fun IrBuilderWithScope.irBlock(
startOffset: Int = this.startOffset,
@@ -645,6 +645,9 @@ class RenderIrElementVisitor(private val normalizeNames: Boolean = false) : IrEl
"type=${expression.type.render()} origin=${expression.origin} " +
"reflectionTarget=${renderReflectionTarget(expression)}"
override fun visitRawFunctionReference(expression: IrRawFunctionReference, data: Nothing?): String =
"RAW_FUNCTION_REFERENCE '${expression.symbol.renderReference()}' type=${expression.type.render()}"
private fun renderReflectionTarget(expression: IrFunctionReference) =
if (expression.symbol == expression.reflectionTarget)
"<same>"
@@ -0,0 +1,23 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
fun interface KRunnable {
fun run()
}
fun runIt(kr: KRunnable) {
kr.run()
}
class C(var value: String) {
fun fn() {
value = "OK"
}
}
fun box(): String {
val c = C("xxx")
runIt(c::fn)
return c.value
}
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
fun interface KRunnable {
fun run()
}
fun runIt(kr: KRunnable) {
kr.run()
}
class C(var value: String) {
fun test(): String {
runIt { value = "OK" }
return value
}
}
fun box() = C("xxx").test()
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
fun interface KRunnable {
fun run()
}
fun runIt(kr: KRunnable) {
kr.run()
}
class C(var value: String)
fun C.test(): String {
runIt { value = "OK" }
return value
}
fun box() = C("xxx").test()
@@ -0,0 +1,20 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
fun interface KRunnable {
fun run()
}
fun runIt(r: KRunnable) {
r.run()
}
var test = "Failed"
fun box(): String {
val ok = "OK"
runIt { test = ok }
return test
}
@@ -0,0 +1,18 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// FILE: capturingIndySam.kt
var test = "Failed"
fun box(): String {
val ok = "OK"
J.run { test = ok }
return test
}
// FILE: J.java
public class J {
public static void run(Runnable r) {
r.run();
}
}
@@ -0,0 +1,46 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// FILE: primitiveVsWrapperInSam.kt
var test = 0
fun tf2(k: Int) { test = k * 10 }
fun tf4() = 5678
fun box(): String {
J.accept42 { k: Int -> test = k }
if (test != 42) return "Failed 1: test=$test"
J.accept42(::tf2)
if (test != 420) return "Failed 2: test=$test"
val t3 = J.get { 1234 }
if (t3 != 1234) return "Failed 3: t3=$t3"
val t4 = J.get(::tf4)
if (t4 != 5678) return "Failed 4: t4=$t4"
return "OK"
}
// FILE: J.java
public class J {
public static void accept42(Sam1 sam) {
sam.accept(42);
}
public static int get(Sam2 sam) {
return sam.get();
}
}
// FILE: Sam1.java
public interface Sam1 {
void accept(Integer x);
}
// FILE: Sam2.java
public interface Sam2 {
Integer get();
}
@@ -0,0 +1,18 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
fun interface KRunnable {
fun run()
}
fun runIt(r: KRunnable) {
r.run()
}
var test = "Failed"
fun box(): String {
runIt { test = "OK" }
return test
}
@@ -0,0 +1,17 @@
// TARGET_BACKEND: JVM
// JVM_TARGET: 1.8
// SAM_CONVERSIONS: INDY
// FILE: simpleIndySam.kt
var test = "Failed"
fun box(): String {
J.run { test = "OK" }
return test
}
// FILE: J.java
public class J {
public static void run(Runnable r) {
r.run();
}
}
@@ -18379,6 +18379,74 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
public class Invokedynamic extends AbstractBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
public class Sam extends AbstractBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@Test
@TestMetadata("boundReference.kt")
public void testBoundReference() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/boundReference.kt");
}
@Test
@TestMetadata("capturedDispatchReceiver.kt")
public void testCapturedDispatchReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedDispatchReceiver.kt");
}
@Test
@TestMetadata("capturedExtensionReceiver.kt")
public void testCapturedExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedExtensionReceiver.kt");
}
@Test
@TestMetadata("capturingIndyFunInterface.kt")
public void testCapturingIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndyFunInterface.kt");
}
@Test
@TestMetadata("capturingIndySam.kt")
public void testCapturingIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndySam.kt");
}
@Test
@TestMetadata("primitiveVsWrapperInSam.kt")
public void testPrimitiveVsWrapperInSam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("simpleIndyFunInterface.kt")
public void testSimpleIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndyFunInterface.kt");
}
@Test
@TestMetadata("simpleIndySam.kt")
public void testSimpleIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
}
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@@ -18379,6 +18379,74 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
public class Invokedynamic extends AbstractIrBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Nested
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
public class Sam extends AbstractIrBlackBoxCodegenTest {
@Test
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("boundReference.kt")
public void testBoundReference() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/boundReference.kt");
}
@Test
@TestMetadata("capturedDispatchReceiver.kt")
public void testCapturedDispatchReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedDispatchReceiver.kt");
}
@Test
@TestMetadata("capturedExtensionReceiver.kt")
public void testCapturedExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedExtensionReceiver.kt");
}
@Test
@TestMetadata("capturingIndyFunInterface.kt")
public void testCapturingIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndyFunInterface.kt");
}
@Test
@TestMetadata("capturingIndySam.kt")
public void testCapturingIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndySam.kt");
}
@Test
@TestMetadata("primitiveVsWrapperInSam.kt")
public void testPrimitiveVsWrapperInSam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@Test
@TestMetadata("simpleIndyFunInterface.kt")
public void testSimpleIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndyFunInterface.kt");
}
@Test
@TestMetadata("simpleIndySam.kt")
public void testSimpleIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
}
}
}
@Nested
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.test.directives
import org.jetbrains.kotlin.config.JvmSamConversions
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.handlers.BytecodeTextHandler
import org.jetbrains.kotlin.test.backend.handlers.IrPrettyKotlinDumpHandler
@@ -90,4 +91,6 @@ object CodegenTestDirectives : SimpleDirectivesContainer() {
val TREAT_AS_ONE_FILE by directive(
description = "Treat bytecode from all files as one in ${BytecodeTextHandler::class}"
)
val SAM_CONVERSIONS by enumDirective<JvmSamConversions>("SAM conversion code generation scheme")
}
@@ -39,3 +39,14 @@ abstract class AbstractJvmBlackBoxCodegenTestBase<R : ResultingArtifact.Frontend
useAfterAnalysisCheckers(::BlackBoxCodegenSuppressor)
}
}
// This class configures various JVM-specific codegen minutiae.
// Ideally, test directives could take care of such things themselves.
class JvmCodegenEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
val samConversions = module.directives[CodegenTestDirectives.SAM_CONVERSIONS].lastOrNull()
if (samConversions != null) {
configuration.put(JVMConfigurationKeys.SAM_CONVERSIONS, samConversions)
}
}
}
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.ASSERTIONS_MODE
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.CONSTRUCTOR_CALL_NORMALIZATION_MODE
@@ -17,7 +17,6 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.TestHelperGeneratorKt;
import org.jetbrains.kotlin.TestsCompilerError;
import org.jetbrains.kotlin.TestsCompiletimeError;
import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection;
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil;
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys;
@@ -40,19 +39,9 @@ import org.jetbrains.kotlin.test.*;
import org.jetbrains.kotlin.test.clientserver.TestProxy;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.tree.ClassNode;
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer;
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
import org.jetbrains.org.objectweb.asm.tree.analysis.SimpleVerifier;
import org.jetbrains.org.objectweb.asm.util.Textifier;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
@@ -16109,6 +16109,72 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Invokedynamic extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Sam extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("boundReference.kt")
public void testBoundReference() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/boundReference.kt");
}
@TestMetadata("capturedDispatchReceiver.kt")
public void testCapturedDispatchReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedDispatchReceiver.kt");
}
@TestMetadata("capturedExtensionReceiver.kt")
public void testCapturedExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturedExtensionReceiver.kt");
}
@TestMetadata("capturingIndyFunInterface.kt")
public void testCapturingIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndyFunInterface.kt");
}
@TestMetadata("capturingIndySam.kt")
public void testCapturingIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/capturingIndySam.kt");
}
@TestMetadata("primitiveVsWrapperInSam.kt")
public void testPrimitiveVsWrapperInSam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/primitiveVsWrapperInSam.kt");
}
@TestMetadata("simpleIndyFunInterface.kt")
public void testSimpleIndyFunInterface() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndyFunInterface.kt");
}
@TestMetadata("simpleIndySam.kt")
public void testSimpleIndySam() throws Exception {
runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleIndySam.kt");
}
}
}
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -13879,6 +13879,32 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
}
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Invokedynamic extends AbstractIrJsCodegenBoxES6Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
}
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Sam extends AbstractIrJsCodegenBoxES6Test {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath);
}
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -13879,6 +13879,32 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Invokedynamic extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Sam extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -13944,6 +13944,32 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Invokedynamic extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Sam extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -8040,6 +8040,32 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
}
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Invokedynamic extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInInvokedynamic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
@TestMetadata("compiler/testData/codegen/box/invokedynamic/sam")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Sam extends AbstractIrCodegenBoxWasmTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath);
}
public void testAllFilesPresentInSam() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true);
}
}
}
@TestMetadata("compiler/testData/codegen/box/ir")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)