diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index 21806940488..978d241f08c 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java @@ -43521,6 +43521,100 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("fieldAccessors.kt") + public void testFieldAccessors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt"); + } + + @Test + @TestMetadata("functionsWithMfvcParameters.kt") + public void testFunctionsWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("internalPrimaryValOfMfvc.kt") + public void testInternalPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("jvmStaticFieldInObject.kt") + public void testJvmStaticFieldInObject() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt"); + } + + @Test + @TestMetadata("jvmStaticFunction.kt") + public void testJvmStaticFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt"); + } + + @Test + @TestMetadata("mfvcConstructor.kt") + public void testMfvcConstructor() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt"); + } + + @Test + @TestMetadata("nonOverridingFunOfMfvc.kt") + public void testNonOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("nonOverridingVarOfMfvc.kt") + public void testNonOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingFunOfMfvc.kt") + public void testOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingVarOfMfvc.kt") + public void testOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("primaryValOfMfvc.kt") + public void testPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("properties.kt") + public void testProperties() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt"); + } + + @Test + @TestMetadata("suspendFunction.kt") + public void testSuspendFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt"); + } + } } @Nested @@ -43640,6 +43734,24 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/callBy/manyMaskArguments.kt"); } + @Test + @TestMetadata("mfvcDefaultArguments.kt") + public void testMfvcDefaultArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt"); + } + + @Test + @TestMetadata("mfvcFunctionsAndConstructors.kt") + public void testMfvcFunctionsAndConstructors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt"); + } + + @Test + @TestMetadata("mfvcMembers.kt") + public void testMfvcMembers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt"); + } + @Test @TestMetadata("nonDefaultParameterOmitted.kt") public void testNonDefaultParameterOmitted() throws Exception { @@ -44484,6 +44596,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt"); } + @Test + @TestMetadata("reflectOnDefaultWithMfvcArgument.kt") + public void testReflectOnDefaultWithMfvcArgument() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt"); + } + @Test @TestMetadata("reflectOnLambdaInArrayConstructor.kt") public void testReflectOnLambdaInArrayConstructor() throws Exception { @@ -44554,6 +44672,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithInlineClassParameters.kt"); } + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt"); + } + @Test @TestMetadata("extensionProperty.kt") public void testExtensionProperty() throws Exception { @@ -44815,6 +44939,18 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt"); } + @Test + @TestMetadata("mfvcInSignature.kt") + public void testMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt"); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt"); + } + @Test @TestMetadata("overrideAnyWithPrimitive.kt") public void testOverrideAnyWithPrimitive() throws Exception { @@ -44881,6 +45017,28 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/mapping/types/withNullability.kt"); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt"); + } + + @Test + @TestMetadata("suspendFunctionWithMfvcInSignature.kt") + public void testSuspendFunctionWithMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt"); + } + } } @Nested @@ -45094,6 +45252,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/modifiers/mfvc.kt"); + } + @Test @TestMetadata("properties.kt") public void testProperties() throws Exception { @@ -45904,6 +46068,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/mfvc.kt"); + } + @Test @TestMetadata("multipleLayers.kt") public void testMultipleLayers() throws Exception { @@ -46013,6 +46183,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt"); + } + @Test @TestMetadata("mutableCollections_after.kt") public void testMutableCollections_after() throws Exception { diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index 5ae50c5eb2b..d1611baf12b 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java @@ -43521,6 +43521,100 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("fieldAccessors.kt") + public void testFieldAccessors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt"); + } + + @Test + @TestMetadata("functionsWithMfvcParameters.kt") + public void testFunctionsWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("internalPrimaryValOfMfvc.kt") + public void testInternalPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("jvmStaticFieldInObject.kt") + public void testJvmStaticFieldInObject() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt"); + } + + @Test + @TestMetadata("jvmStaticFunction.kt") + public void testJvmStaticFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt"); + } + + @Test + @TestMetadata("mfvcConstructor.kt") + public void testMfvcConstructor() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt"); + } + + @Test + @TestMetadata("nonOverridingFunOfMfvc.kt") + public void testNonOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("nonOverridingVarOfMfvc.kt") + public void testNonOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingFunOfMfvc.kt") + public void testOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingVarOfMfvc.kt") + public void testOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("primaryValOfMfvc.kt") + public void testPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("properties.kt") + public void testProperties() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt"); + } + + @Test + @TestMetadata("suspendFunction.kt") + public void testSuspendFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt"); + } + } } @Nested @@ -43640,6 +43734,24 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/callBy/manyMaskArguments.kt"); } + @Test + @TestMetadata("mfvcDefaultArguments.kt") + public void testMfvcDefaultArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt"); + } + + @Test + @TestMetadata("mfvcFunctionsAndConstructors.kt") + public void testMfvcFunctionsAndConstructors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt"); + } + + @Test + @TestMetadata("mfvcMembers.kt") + public void testMfvcMembers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt"); + } + @Test @TestMetadata("nonDefaultParameterOmitted.kt") public void testNonDefaultParameterOmitted() throws Exception { @@ -44484,6 +44596,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt"); } + @Test + @TestMetadata("reflectOnDefaultWithMfvcArgument.kt") + public void testReflectOnDefaultWithMfvcArgument() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt"); + } + @Test @TestMetadata("reflectOnLambdaInArrayConstructor.kt") public void testReflectOnLambdaInArrayConstructor() throws Exception { @@ -44554,6 +44672,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithInlineClassParameters.kt"); } + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt"); + } + @Test @TestMetadata("extensionProperty.kt") public void testExtensionProperty() throws Exception { @@ -44815,6 +44939,18 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt"); } + @Test + @TestMetadata("mfvcInSignature.kt") + public void testMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt"); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt"); + } + @Test @TestMetadata("overrideAnyWithPrimitive.kt") public void testOverrideAnyWithPrimitive() throws Exception { @@ -44881,6 +45017,28 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/mapping/types/withNullability.kt"); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt"); + } + + @Test + @TestMetadata("suspendFunctionWithMfvcInSignature.kt") + public void testSuspendFunctionWithMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt"); + } + } } @Nested @@ -45094,6 +45252,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/modifiers/mfvc.kt"); + } + @Test @TestMetadata("properties.kt") public void testProperties() throws Exception { @@ -45904,6 +46068,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/mfvc.kt"); + } + @Test @TestMetadata("multipleLayers.kt") public void testMultipleLayers() throws Exception { @@ -46013,6 +46183,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt"); + } + @Test @TestMetadata("mutableCollections_after.kt") public void testMutableCollections_after() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index 7b2d4ad6f38..79c96ea696b 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -105,15 +105,14 @@ internal class JvmMultiFieldValueClassLowering( return expressions.subList(0, expressions.size - repeatable.size) to repeatable } - private fun IrBuilderWithScope.castedToNotNull(expression: IrExpression) = - if (expression.type.isNullable()) irImplicitCast(expression, expression.type.makeNotNull()) else expression - fun IrBlockBuilder.addReplacement(expression: IrSetValue, safe: Boolean): IrExpression? { oldValueSymbol2NewValueSymbol[expression.symbol]?.let { return irSet(it.owner, expression.value).also { irSet -> +irSet } } val instance = oldSymbol2MfvcNodeInstance[expression.symbol] ?: return null - val values: List = makeFlattenedExpressionsWithGivenSafety(instance.node, safe, castedToNotNull(expression.value)) + val values: List = makeFlattenedExpressionsWithGivenSafety( + instance.node, safe, castExpressionToNotNullTypeIfNeeded(expression.value, instance.type) + ) val setterExpressions = instance.makeSetterExpressions(this, values) expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values) +setterExpressions @@ -166,7 +165,9 @@ internal class JvmMultiFieldValueClassLowering( accessType = AccessType.AlwaysPrivate, saveVariable = ::variablesSaver ) - val values: List = makeFlattenedExpressionsWithGivenSafety(node, safe, castedToNotNull(expression.value)) + val values: List = makeFlattenedExpressionsWithGivenSafety( + node, safe, castExpressionToNotNullTypeIfNeeded(expression.value, node.type) + ) val setterExpressions = instance.makeSetterExpressions(this, values) expression2MfvcNodeInstanceAccessor[setterExpressions] = MfvcNodeInstanceAccessor.Setter(instance, values) +setterExpressions @@ -318,6 +319,12 @@ internal class JvmMultiFieldValueClassLowering( if (replacingDeclaration in possibleExtraBoxUsageGenerated) removeAllExtraBoxes() } as IrBlockBody + is IrField -> replacingDeclaration.initializer = replacingDeclaration.initializer?.makeBodyWithAddedVariables( + context, variablesToAdd[replacingDeclaration] ?: emptySet(), replacingDeclaration.symbol + )?.apply { + if (replacingDeclaration in possibleExtraBoxUsageGenerated) removeAllExtraBoxes() + } as IrExpressionBody? + else -> Unit } } @@ -326,13 +333,15 @@ internal class JvmMultiFieldValueClassLowering( irClass.primaryConstructor?.let { replacements.getReplacementForRegularClassConstructor(it)?.let { replacement -> addBindingsFor(it, replacement) } } - val propertiesOrFields = collectPropertiesOrFieldsAfterLowering(irClass) - val oldBackingFields = propertiesOrFields.mapNotNull { propertyOrField -> - val property = (propertyOrField as? IrPropertyOrIrField.Property)?.property ?: return@mapNotNull null - property.backingField?.let { property to it } - }.toMap() - val propertiesOrFieldsReplacement = - collectRegularClassMfvcPropertiesOrFieldsReplacement(propertiesOrFields) // resets backing fields + val propertiesOrFields = collectPropertiesOrFieldsAfterLowering(irClass, context) + val oldBackingFields = buildMap { + for (propertyOrField in propertiesOrFields) { + val property = (propertyOrField as? IrPropertyOrIrField.Property)?.property ?: continue + val field = property.backingField ?: continue + put(property, field) + } + } + val propertiesOrFieldsReplacement = collectRegularClassMfvcPropertiesOrFieldsReplacement(propertiesOrFields) val fieldsToRemove = propertiesOrFieldsReplacement.keys.mapNotNull { when (it) { @@ -390,12 +399,15 @@ internal class JvmMultiFieldValueClassLowering( context.irFactory.createAnonymousInitializer( startOffset = UNDEFINED_OFFSET, endOffset = UNDEFINED_OFFSET, origin = IrDeclarationOrigin.GENERATED_MULTI_FIELD_VALUE_CLASS_MEMBER, - symbol = IrAnonymousInitializerSymbolImpl() + symbol = IrAnonymousInitializerSymbolImpl(), + isStatic = element.isStatic, ).apply { parent = irClass body = context.createJvmIrBuilder(symbol).irBlockBody { +irSetField( - irClass.thisReceiver!!.takeUnless { element.isStatic }?.let { irGet(it) }, element, initializer.expression, + receiver = irClass.thisReceiver!!.takeUnless { element.isStatic }?.let { irGet(it) }, + field = element, + value = initializer.expression.patchDeclarationParents(irClass), origin = UNSAFE_MFVC_SET_ORIGIN ) } @@ -800,18 +812,18 @@ internal class JvmMultiFieldValueClassLowering( } override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { - val originalFunction = expression.symbol.owner - if (originalFunction.getReplacement() == null) return super.visitFunctionReference(expression) - return makeNewLambda(originalFunction, expression, makeBody = { wrapper -> - with(context.createJvmIrBuilder(wrapper.symbol)) { - irExprBody(irCall(originalFunction).apply { - passTypeArgumentsFrom(wrapper) - for ((newParam, originalParam) in wrapper.explicitParameters zip originalFunction.explicitParameters) { - putArgument(originalParam, irGet(newParam)) - } - }).transform(this@JvmMultiFieldValueClassLowering, null) + val function = expression.symbol.owner + val replacement = function.getReplacement() ?: return super.visitFunctionReference(expression) + return context.createJvmIrBuilder(expression.symbol, expression).irBlock { + // Bridge call is added in BridgeLowering + buildReplacement(function, expression, replacement) { + IrFunctionReferenceImpl( + expression.startOffset, expression.endOffset, + expression.type, replacement.symbol, function.typeParameters.size, replacement.valueParameters.size, + expression.reflectionTarget, expression.origin + ).copyAttributes(expression) } - }) + }.unwrapBlock() } private fun IrFunction.getReplacement(): IrFunction? = @@ -995,7 +1007,7 @@ internal class JvmMultiFieldValueClassLowering( require(parameter2expression.size == structure.size) require(structure.sumOf { it.valueParameters.size } == replacement.explicitParametersCount) val newArguments: List = - makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure.map { it.valueParameters }) + makeNewArguments(parameter2expression.map { (_, argument) -> argument }, structure) val resultExpression = makeMemberAccessExpression(replacement.symbol).apply { passTypeArgumentsWithOffsets(replacement, originalFunction) { original.getTypeArgument(it)!! } for ((parameter, argument) in replacement.explicitParameters zip newArguments) { @@ -1023,21 +1035,15 @@ internal class JvmMultiFieldValueClassLowering( return super.visitStringConcatenation(expression) } - private fun IrBlockBuilder.makeNewArguments( - oldArguments: List, - structure: List> - ): List { - val argumentSizes: List = structure.map { argTemplate -> argTemplate.size } - val newArguments = (oldArguments zip argumentSizes).flatMap { (oldArgument, parametersCount) -> + private fun IrBlockBuilder.makeNewArguments(oldArguments: List, structure: List): List { + val argumentSizes: List = structure.map { argTemplate -> argTemplate.valueParameters.size } + val newArguments = (oldArguments zip argumentSizes).flatMapIndexed { index, (oldArgument, parametersCount) -> when { oldArgument == null -> List(parametersCount) { null } parametersCount == 1 -> listOf(oldArgument.transform(this@JvmMultiFieldValueClassLowering, null)) else -> { - val castedIfNeeded = when { - oldArgument.type.needsMfvcFlattening() -> oldArgument - oldArgument.type.makeNotNull().needsMfvcFlattening() -> irImplicitCast(oldArgument, oldArgument.type.makeNotNull()) - else -> error("Unexpected type: ${oldArgument.type.render()}") - } + val expectedType = (structure[index] as MultiFieldValueClassMapping).boxedType + val castedIfNeeded = castExpressionToNotNullTypeIfNeeded(oldArgument, expectedType) flattenExpression(castedIfNeeded).also { require(it.size == parametersCount) { "Expected $parametersCount arguments but got ${it.size}" } } @@ -1047,6 +1053,12 @@ internal class JvmMultiFieldValueClassLowering( return newArguments } + private fun IrBuilderWithScope.castExpressionToNotNullTypeIfNeeded(expression: IrExpression, type: IrType) = when (type) { + expression.type -> expression + expression.type.makeNotNull() -> irImplicitCast(expression, type) + else -> irAs(expression, type) + } + /** * Inlines initialization of variables when possible and returns their values * @@ -1212,7 +1224,7 @@ internal class JvmMultiFieldValueClassLowering( ) val type = if (expression is IrConstructorCall) expression.symbol.owner.constructedClass.defaultType else expression.type val lowering = this@JvmMultiFieldValueClassLowering - if (rootNode == null || !type.needsMfvcFlattening()) { + if (rootNode == null || !type.needsMfvcFlattening() || instance.size == 1) { require(instance.size == 1) { "Required 1 variable/field to store regular value but got ${instance.size}" } instance.addSetterStatements(this, listOf(expression.transform(lowering, null))) return @@ -1263,11 +1275,7 @@ internal class JvmMultiFieldValueClassLowering( } } val nullableTransformedExpression = expression.transform(this@JvmMultiFieldValueClassLowering, null) - val transformedExpression = - if (nullableTransformedExpression.type.isNullable()) - irImplicitCast(nullableTransformedExpression, nullableTransformedExpression.type.makeNotNull()) - else - nullableTransformedExpression + val transformedExpression = castExpressionToNotNullTypeIfNeeded(nullableTransformedExpression, instance.type) val addedSettersToFlattened = valueDeclarationsRemapper.handleFlattenedGetterExpressions(this, transformedExpression) { require(it.size == instance.size) { "Incompatible assignment sizes: ${it.size}, ${instance.size}" } instance.makeSetterExpressions(this, it) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt index 2fec1b8867b..e4d814e9155 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MfvcNodeFactory.kt @@ -32,7 +32,6 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.OperatorNameConventions @@ -61,7 +60,7 @@ fun createLeafMfvcNode( this.name = fullFieldName this.type = type this.visibility = DescriptorVisibilities.PRIVATE - this.metadata = null + oldBackingField.metadata = null }.apply { this.parent = oldBackingField.parent this.annotations = fieldAnnotations.map { it.deepCopyWithVariables() } @@ -246,19 +245,27 @@ fun createIntermediateMfvcNode( ) } -fun collectPropertiesAfterLowering(irClass: IrClass): LinkedHashSet = - LinkedHashSet(collectPropertiesOrFieldsAfterLowering(irClass).map { (it as Property).property }) +fun collectPropertiesAfterLowering(irClass: IrClass, context: JvmBackendContext): LinkedHashSet = + LinkedHashSet(collectPropertiesOrFieldsAfterLowering(irClass, context).map { (it as Property).property }) sealed class IrPropertyOrIrField { data class Property(val property: IrProperty) : IrPropertyOrIrField() data class Field(val field: IrField) : IrPropertyOrIrField() } -fun collectPropertiesOrFieldsAfterLowering(irClass: IrClass): LinkedHashSet = +fun collectPropertiesOrFieldsAfterLowering(irClass: IrClass, context: JvmBackendContext): LinkedHashSet = LinkedHashSet().apply { for (element in irClass.declarations) { if (element is IrField) { - element.correspondingPropertySymbol?.owner?.takeUnless { it.isDelegated }?.let { add(Property(it)) } ?: add(Field(element)) + val property = element.correspondingPropertySymbol?.owner + if ( + property != null && !property.isDelegated && + !context.multiFieldValueClassReplacements.getFieldsToRemove(element.parentAsClass).contains(element) + ) { + add(Property(property)) + } else { + add(Field(element)) + } } else if (element is IrSimpleFunction && element.extensionReceiverParameter == null && element.contextReceiverParametersCount == 0) { element.correspondingPropertySymbol?.owner?.let { add(Property(it)) } } @@ -275,7 +282,7 @@ fun getRootNode(context: JvmBackendContext, mfvc: IrClass): RootMfvcNode { val oldPrimaryConstructor = mfvc.primaryConstructor!! val oldFields = mfvc.fields.filter { !it.isStatic }.toList() val representation = mfvc.multiFieldValueClassRepresentation!! - val properties = collectPropertiesAfterLowering(mfvc).associateBy { it.isStatic(mfvc) to it.name } + val properties = collectPropertiesAfterLowering(mfvc, context).associateBy { it.isStatic(mfvc) to it.name } val subnodes = makeRootMfvcNodeSubnodes(representation, properties, context, mfvc) @@ -394,6 +401,11 @@ private fun makePrimaryConstructorImpl( } } annotations = oldPrimaryConstructor.annotations + if (oldPrimaryConstructor.metadata != null) { + metadata = oldPrimaryConstructor.metadata + oldPrimaryConstructor.metadata = null + } + copyAttributes(oldPrimaryConstructor as? IrAttributeContainer) // body is added in the Lowering file as it needs to be lowered } @@ -448,20 +460,18 @@ private fun makeRootMfvcNodeSubnodes( Modality.FINAL, oldBackingField, ).also { - updateAnnotationsAndPropertyFromOldProperty(oldProperty) + updateAnnotationsAndPropertyFromOldProperty(oldProperty, context, it) it.unboxMethod.overriddenSymbols = listOf() // the getter is saved so it overrides itself } } private fun updateAnnotationsAndPropertyFromOldProperty( - oldProperty: IrProperty + oldProperty: IrProperty, + context: JvmBackendContext, + node: MfvcNode, ) { - oldProperty.setter?.apply { - name = Name.identifier(JvmAbi.setterName(oldProperty.name.asString())) - correspondingPropertySymbol = null - origin = IrDeclarationOrigin.DEFINED - } - oldProperty.setter = null + if (node is LeafMfvcNode) return + oldProperty.backingField?.let { context.multiFieldValueClassReplacements.addFieldToRemove(it.parentAsClass, it) } oldProperty.backingField = null } @@ -482,7 +492,7 @@ fun createIntermediateNodeForMfvcPropertyOfRegularClass( parent, context, type, makeTypeArgumentsFromType(type), MethodFullNameMode.Getter, listOf(oldProperty.name), fieldAnnotations, static, overriddenNode, null, oldGetter, modality, oldField ).also { - updateAnnotationsAndPropertyFromOldProperty(oldProperty) + updateAnnotationsAndPropertyFromOldProperty(oldProperty, context, it) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt index a4a995f224f..3d2490d5f60 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt @@ -243,9 +243,11 @@ fun IrSimpleFunction.copyCorrespondingPropertyFrom(source: IrSimpleFunction) { fun IrProperty.needsAccessor(accessor: IrSimpleFunction): Boolean = when { // Properties in annotation classes become abstract methods named after the property. (parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS -> true - // Multi-field value class getters must always be added. Getters for properties of MFVC itself follow general rules. + // Multi-field value class accessors must always be added. accessor.isGetter && accessor.contextReceiverParametersCount == 0 && accessor.extensionReceiverParameter == null && - !accessor.parent.let { it is IrClass && it.isMultiFieldValueClass } && accessor.returnType.needsMfvcFlattening() -> true + accessor.returnType.needsMfvcFlattening() -> true + accessor.isSetter && accessor.contextReceiverParametersCount == 0 && accessor.extensionReceiverParameter == null && + accessor.valueParameters.single().type.needsMfvcFlattening() -> true // @JvmField properties have no getters/setters resolveFakeOverride()?.backingField?.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME) == true -> false // We do not produce default accessors for private fields diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt new file mode 100644 index 00000000000..996e5b32f7b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt @@ -0,0 +1,51 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + + +@JvmInline +value class Z(val x1: UInt, val x2: Int) + +class Outer(val z1: Z, val z2: Z?) { + inner class Inner(val z3: Z, val z4: Z?) { + val test = "$z1 $z2 $z3 $z4" + } +} + +@JvmInline +value class ValueNonNullOuter(val z11: Z, val z12: Z) { + class Inner(val t: ValueNonNullOuter, val z2: Z, val z3: Z?) { + val test = "${t.z11} ${t.z12} $z2 $z3" + } +} + +@JvmInline +value class ValueNullableOuter(val z11: Z?, val z12: Z?) { + class Inner(val t: ValueNullableOuter, val z2: Z, val z3: Z?) { + val test = "${t.z11} ${t.z12} $z2 $z3" + } +} + +fun box(): String { + val z1 = Z(1U, -1) + val z2 = Z(2U, -2) + val z3 = Z(3U, -3) + val z4 = Z(4U, -4) + + val outer = ::Outer.call(z1, z2) + assertEquals(z1, outer.z1) + assertEquals(z2, outer.z2) + + assertEquals("Z(x1=1, x2=-1) Z(x1=2, x2=-2) Z(x1=3, x2=-3) Z(x1=4, x2=-4)", Outer::Inner.call(outer, z3, z4).test) + assertEquals("Z(x1=1, x2=-1) Z(x1=2, x2=-2) Z(x1=2, x2=-2) Z(x1=4, x2=-4)", outer::Inner.call(z2, z4).test) + + val valueNonNullOuter = ValueNonNullOuter(z1, z4) + assertEquals("Z(x1=1, x2=-1) Z(x1=4, x2=-4) Z(x1=2, x2=-2) Z(x1=3, x2=-3)", ValueNonNullOuter::Inner.call(valueNonNullOuter, z2, z3).test) + + val valueNullableOuter = ValueNullableOuter(z1, z4) + assertEquals("Z(x1=1, x2=-1) Z(x1=4, x2=-4) Z(x1=2, x2=-2) Z(x1=3, x2=-3)", ValueNullableOuter::Inner.call(valueNullableOuter, z2, z3).test) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt new file mode 100644 index 00000000000..20b97fd9976 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt @@ -0,0 +1,59 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.jvm.isAccessible +import kotlin.test.assertEquals + +@JvmInline +value class S(val value1: UInt, val value2: Int) { + operator fun plus(other: S): S = S(this.value1 + other.value1, this.value2 + other.value2) +} + +class C { + private var nonNullMember: S = S(UInt.MAX_VALUE, -10) + private var nullableMember: S? = S(UInt.MAX_VALUE, -10) + + fun nonNullUnboundRef() = C::nonNullMember.apply { isAccessible = true } + fun nonNullBoundRef() = this::nonNullMember.apply { isAccessible = true } + fun nullableUnboundRef() = C::nullableMember.apply { isAccessible = true } + fun nullableBoundRef() = this::nullableMember.apply { isAccessible = true } +} + +private var nonNullTopLevel: S = S(UInt.MAX_VALUE, -10) +private var nullableTopLevel: S? = S(UInt.MAX_VALUE, -10) + +fun box(): String { + val c = C() + val zero = S(0U, 5) + val one = S(1U, 10) + val two = S(2U, 20) + + assertEquals(Unit, c.nonNullUnboundRef().setter.call(c, zero)) + assertEquals(zero, c.nonNullUnboundRef().call(c)) + assertEquals(zero, c.nonNullUnboundRef().getter.call(c)) + + assertEquals(Unit, c.nonNullBoundRef().setter.call(one)) + assertEquals(one, c.nonNullBoundRef().call()) + assertEquals(one, c.nonNullBoundRef().getter.call()) + + assertEquals(Unit, c.nullableUnboundRef().setter.call(c, zero)) + assertEquals(zero, c.nullableUnboundRef().call(c)) + assertEquals(zero, c.nullableUnboundRef().getter.call(c)) + + assertEquals(Unit, c.nullableBoundRef().setter.call(one)) + assertEquals(one, c.nullableBoundRef().call()) + assertEquals(one, c.nullableBoundRef().getter.call()) + + val nonNullTopLevel = ::nonNullTopLevel.apply { isAccessible = true } + assertEquals(Unit, nonNullTopLevel.setter.call(two)) + assertEquals(two, nonNullTopLevel.call()) + assertEquals(two, nonNullTopLevel.getter.call()) + + val nullableTopLevel = ::nullableTopLevel.apply { isAccessible = true } + assertEquals(Unit, nullableTopLevel.setter.call(two)) + assertEquals(two, nullableTopLevel.call()) + assertEquals(two, nullableTopLevel.getter.call()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt new file mode 100644 index 00000000000..e157ba226f3 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt @@ -0,0 +1,53 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +@JvmInline +value class S(val value1: UInt, val value2: Int) { + operator fun plus(other: S): S = S(this.value1 + other.value1, this.value2 + other.value2) +} + +class C { + fun member(x: S, y1: UInt, y2: Int, z: S?): S = x + S(y1, y2) + z!! +} + +fun topLevel(x1: UInt, x2: Int, y: S, z: S?): S = S(x1, x2) + y + z!! + +fun S.extension1(y: S, z: S?): S = this + y + z!! + +fun S?.extension2(y: S, z: S?) = this!! + y + z!! + +fun S.extension3_1(): UInt = value1 +fun S.extension3_2(): Int = value2 + +fun S?.extension4_1(): UInt = this!!.value1 +fun S?.extension4_2(): Int = this!!.value2 + +fun box(): String { + val zero = S(0U, 1000) + val one = S(1U, -1) + val two = S(2U, -2) + val four = S(4U, -4) + val seven = S(7U, -7) + + assertEquals(seven, C::member.call(C(), one, 2U, -2, four)) + assertEquals(seven, ::topLevel.call(1U, -1, two, four)) + assertEquals(seven, S::extension1.call(one, two, four)) + assertEquals(seven, S::extension2.call(one, two, four)) + assertEquals(0U, S::extension3_1.call(zero)) + assertEquals(1000, S::extension3_2.call(zero)) + assertEquals(0U, S?::extension4_1.call(zero)) + assertEquals(1000, S?::extension4_2.call(zero)) + + assertEquals(seven, C()::member.call(one, 2U, -2, four)) + assertEquals(seven, one::extension1.call(two, four)) + assertEquals(seven, one::extension2.call(two, four)) + assertEquals(0U, zero::extension3_1.call()) + assertEquals(1000, zero::extension3_2.call()) + assertEquals(0U, zero::extension4_1.call()) + assertEquals(1000, zero::extension4_2.call()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt new file mode 100644 index 00000000000..681b22321c2 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt @@ -0,0 +1,74 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +@JvmInline +value class Z(internal val x1: UInt, internal val x2: Int) +@JvmInline +value class Z2(internal val x1: Z, internal val x2: Z) + +@JvmInline +value class L(internal val x1: ULong, internal val x2: Long) +@JvmInline +value class L2(internal val x1: L, internal val x2: L) + +@JvmInline +value class A1(internal val x1: Any?, internal val x2: Any?) +@JvmInline +value class A1_2(internal val x1: A1, internal val x2: A1) +@JvmInline +value class A2(internal val x1: Any, internal val x2: Any) +@JvmInline +value class A2_2(internal val x1: A2, internal val x2: A2) + +fun box(): String { + assertEquals(42U, Z::x1.call(Z(42U, 43))) + assertEquals(43, Z::x2.call(Z(42U, 43))) + assertEquals(42U, Z(42U, 43)::x1.call()) + assertEquals(43, Z(42U, 43)::x2.call()) + + assertEquals(1234UL, L::x1.call(L(1234UL, 5678L))) + assertEquals(5678L, L::x2.call(L(1234UL, 5678L))) + assertEquals(1234UL, L(1234UL, 5678L)::x1.call()) + assertEquals(5678L, L(1234UL, 5678L)::x2.call()) + + assertEquals("abc", A1::x1.call(A1("abc", "def"))) + assertEquals("def", A1::x2.call(A1("abc", "def"))) + assertEquals("abc", A1("abc", "def")::x1.call()) + assertEquals("def", A1("abc", "def")::x2.call()) + assertEquals(null, A1::x1.call(A1(null, null))) + assertEquals(null, A1::x2.call(A1(null, null))) + assertEquals(null, A1(null, null)::x1.call()) + assertEquals(null, A1(null, null)::x2.call()) + assertEquals("abc", A2::x1.call(A2("abc", "def"))) + assertEquals("def", A2::x2.call(A2("abc", "def"))) + assertEquals("abc", A2("abc", "def")::x1.call()) + assertEquals("def", A2("abc", "def")::x2.call()) + + assertEquals(Z(42U, 43), Z2::x1.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Z(44U, 45), Z2::x2.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Z(42U, 43), Z2(Z(42U, 43), Z(44U, 45))::x1.call()) + assertEquals(Z(44U, 45), Z2(Z(42U, 43), Z(44U, 45))::x2.call()) + + assertEquals(L(1234UL, 5678L), L2::x1.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(L(12340UL, -5678L), L2::x2.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(L(1234UL, 5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x1.call()) + assertEquals(L(12340UL, -5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x2.call()) + + assertEquals(A1("abc", "def"), A1_2::x1.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(A1("geh", "ijk"), A1_2::x2.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(A1("abc", "def"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x1.call()) + assertEquals(A1("geh", "ijk"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x2.call()) + assertEquals(A1(null, null), A1_2::x1.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(A1(null, null), A1_2::x2.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x1.call()) + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x2.call()) + assertEquals(A2("abc", "def"), A2_2::x1.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(A2("geh", "ijk"), A2_2::x2.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(A2("abc", "def"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x1.call()) + assertEquals(A2("geh", "ijk"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x2.call()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt new file mode 100644 index 00000000000..3784db6f784 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt @@ -0,0 +1,53 @@ +// TARGET_BACKEND: JVM_IR +// JVM_TARGET: 1.8 +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.jvm.isAccessible +import kotlin.test.assertEquals + +@JvmInline +value class Z(val value1: UInt, val value2: Int) { + operator fun plus(other: Z): Z = Z(this.value1 + other.value1, this.value2 + other.value2) +} + +object C { + @JvmStatic + private var p1: Z = Z(UInt.MAX_VALUE, -10) + + @JvmStatic + private var p2: Z? = Z(UInt.MAX_VALUE, -10) + + fun nonNullBoundRef() = this::p1.apply { isAccessible = true } + fun nullableBoundRef() = this::p2.apply { isAccessible = true } +} + +fun box(): String { + val one = Z(1U, 10) + val two = Z(2U, 20) + + val nonNullUnboundRef = C::class.members.single { it.name == "p1" } as KMutableProperty1 + nonNullUnboundRef.isAccessible = true + assertEquals(Unit, nonNullUnboundRef.setter.call(C, one)) + assertEquals(one, nonNullUnboundRef.call(C)) + assertEquals(one, nonNullUnboundRef.getter.call(C)) + + val nullableUnboundRef = C::class.members.single { it.name == "p2" } as KMutableProperty1 + nullableUnboundRef.isAccessible = true + assertEquals(Unit, nullableUnboundRef.setter.call(C, one)) + assertEquals(one, nullableUnboundRef.call(C)) + assertEquals(one, nullableUnboundRef.getter.call(C)) + + val nonNullBoundRef = C.nonNullBoundRef() + assertEquals(Unit, nonNullBoundRef.setter.call(two)) + assertEquals(two, nonNullBoundRef.call()) + assertEquals(two, nonNullBoundRef.getter.call()) + + val nullableBoundRef = C.nullableBoundRef() + assertEquals(Unit, nullableBoundRef.setter.call(two)) + assertEquals(two, nullableBoundRef.call()) + assertEquals(two, nullableBoundRef.getter.call()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt new file mode 100644 index 00000000000..d6617faeb0b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt @@ -0,0 +1,42 @@ +// TARGET_BACKEND: JVM_IR +// JVM_TARGET: 1.8 +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.KFunction +import kotlin.test.assertEquals + +@JvmInline +value class Z(val value1: UInt, val value2: Int) { + operator fun plus(other: Z): Z = Z(this.value1 + other.value1, this.value2 + other.value2) +} + +object C { + @JvmStatic + fun foo(x: Z, y1: UInt, y2: Int, z: Z?): Z = x + Z(y1, y2) + z!! +} + +interface I { + companion object { + @JvmStatic + fun bar(x1: UInt, x2: Int, y: Z, z: Z?): Z = Z(x1, x2) + y + z!! + } +} + +fun box(): String { + val one = Z(1U, -1) + val two = Z(2U, -2) + val four = Z(4U, -4) + val seven = Z(7U, -7) + + assertEquals(seven, C::foo.call(one, 2U, -2, four)) + assertEquals(seven, (I)::bar.call(1U, -1, two, four)) + + val unboundFoo = C::class.members.single { it.name == "foo" } as KFunction<*> + assertEquals(seven, unboundFoo.call(C, one, 2U, -2, four)) + + val unboundBar = I.Companion::class.members.single { it.name == "bar" } as KFunction<*> + assertEquals(seven, unboundBar.call(I, 1U, -1, two, four)) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt new file mode 100644 index 00000000000..25a446d6ddf --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt @@ -0,0 +1,82 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.KCallable +import kotlin.test.assertEquals + +@JvmInline +value class Z(val x1: UInt, val x2: Int) { + constructor(a: UInt, b: UInt, c: Int, d: Int) : this(a + b, c + d) +} + +@JvmInline +value class L(val x1: ULong, val x2: Long) { + constructor(a: ULong, b: ULong, c: Long, d: Long) : this(a + b, c + d) +} + +@JvmInline +value class S1(val x1: String, val x2: String) { + constructor(a: String, b: String, c: String, d: String) : this(a + b, c + d) +} + +@JvmInline +value class S2(val x1: String?, val x2: String?) { + constructor(a: String?, b: String?, c: String?, d: String?) : this(a!! + b!!, c!! + d!!) +} + +@JvmInline +value class A(val x1: Any, val x2: Any) { + constructor(a: String, b: String, c: String, d: String) : this(a + b, c + d) +} + +@JvmInline +value class Z2(val z1: Z, val z2: Z) { + constructor(z1: Z, z2: Z, z3: Z, z4: Z) : this(Z(z1.x1 + z2.x1, z1.x2 + z2.x2), Z(z3.x1 + z4.x1, z3.x2 + z4.x2)) +} + +@JvmInline +value class Z3(val z1: Z?, val z2: Z?) { + constructor(z1: Z?, z2: Z?, z3: Z?, z4: Z?) : this(Z(z1!!.x1 + z2!!.x1, z1!!.x2 + z2!!.x2), Z(z3!!.x1 + z4!!.x1, z3!!.x2 + z4!!.x2)) +} + +fun box(): String { + val ctorZ1_1: (UInt, Int) -> Z = ::Z + val ctorZ1_2: (UInt, UInt, Int, Int) -> Z = ::Z + val ctorL1: (ULong, Long) -> L = ::L + val ctorL2: (ULong, ULong, Long, Long) -> L = ::L + val ctorS1_1: (String, String) -> S1 = ::S1 + val ctorS1_2: (String, String, String, String) -> S1 = ::S1 + val ctorS2_1: (String, String) -> S2 = ::S2 + val ctorS2_2: (String, String, String, String) -> S2 = ::S2 + val ctorA1: (Any, Any) -> A = ::A + val ctorA2: (String, String, String, String) -> A = ::A + val ctorZ2_2: (Z, Z) -> Z2 = ::Z2 + val ctorZ2_4: (Z, Z, Z, Z) -> Z2 = ::Z2 + val ctorZ3_2: (Z, Z) -> Z3 = ::Z3 + val ctorZ3_4: (Z, Z, Z, Z) -> Z3 = ::Z3 + + assertEquals(Z(42U, 43), (ctorZ1_1 as KCallable).call(42U, 43)) + assertEquals(Z(123U, 224), (ctorZ1_2 as KCallable).call(100U, 23U, 200, 24)) + assertEquals(L(1UL, 2L), (ctorL1 as KCallable).call(1UL, 2L)) + assertEquals(L(123UL, 224L), (ctorL2 as KCallable).call(100UL, 23UL, 200L, 24L)) + assertEquals(S1("abc", "def"), (ctorS1_1 as KCallable).call("abc", "def")) + assertEquals(S1("abc", "def"), (ctorS1_2 as KCallable).call("ab", "c", "de", "f")) + assertEquals(S2("abc", "def"), (ctorS2_1 as KCallable).call("abc", "def")) + assertEquals(S2("abc", "def"), (ctorS2_2 as KCallable).call("ab", "c", "de", "f")) + assertEquals(A("abc", "def"), (ctorA1 as KCallable).call("abc", "def")) + assertEquals(A("abc", "def"), (ctorA2 as KCallable).call("a", "bc", "d", "ef")) + + assertEquals(Z2(Z(42U, 43), Z(44U, 45)), (ctorZ2_2 as KCallable).call(Z(42U, 43), Z(44U, 45))) + assertEquals(Z3(Z(42U, 43), Z(44U, 45)), (ctorZ3_2 as KCallable).call(Z(42U, 43), Z(44U, 45))) + assertEquals( + Z2(Z(142U, 243), Z(344U, 445)), + (ctorZ2_4 as KCallable).call(Z(42U, 43), Z(100U, 200), Z(44U, 45), Z(300U, 400)) + ) + assertEquals( + Z3(Z(142U, 243), Z(344U, 445)), + (ctorZ3_4 as KCallable).call(Z(42U, 43), Z(100U, 200), Z(44U, 45), Z(300U, 400)) + ) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt new file mode 100644 index 00000000000..0bc1b3074a6 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +@JvmInline +value class Z(val x1: UInt, val x2: Int) { + fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +@JvmInline +value class S(val x1: String, val x2: String) { + fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +@JvmInline +value class A(val x1: Any, val x2: Any) { + fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +fun box(): String { + val two = Z(2U, 3) + val four = Z(4U, 5) + + val expected = "0912345" + assertEquals(expected, Z::test.call(Z(0U, 9), 1, two, four)) + assertEquals(expected, Z(0U, 9)::test.call(1, two, four)) + + assertEquals(expected, S::test.call(S("0", "9"), 1, two, four)) + assertEquals(expected, S("0", "9")::test.call(1, two, four)) + + assertEquals(expected, A::test.call(A(0U, 9), 1, two, four)) + assertEquals(expected, A(0U, 9)::test.call(1, two, four)) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt new file mode 100644 index 00000000000..dd8b4f55053 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt @@ -0,0 +1,127 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +var global = Z(0U, 0) + +@JvmInline +value class Z(val x1: UInt, val x2: Int) { + var nonNullTest: Z + get() = Z(global.x1 + this.x1, global.x2 + this.x2) + set(value) { + global = Z(this.x1 + value.x1, this.x2 + value.x2) + } + + var nullableTest: Z? + get() = Z(global.x1 + this.x1, global.x2 + this.x2) + set(value) { + global = Z(this.x1 + value!!.x1, this.x2 + value!!.x2) + } +} + +@JvmInline +value class S(val x1: String, val x2: String) { + var nonNullTest: Z + get() = Z(global.x1 + x1.toUInt(), global.x2 + x2.toInt()) + set(value) { + global = Z(this.x1.toUInt() + value.x1, this.x2.toInt() + value.x2) + } + + var nullableTest: Z? + get() = Z(global.x1 + x1.toUInt(), global.x2 + x2.toInt()) + set(value) { + global = Z(this.x1.toUInt() + value!!.x1, this.x2.toInt() + value!!.x2) + } +} + +@JvmInline +value class A(val x1: Any, val x2: Any) { + var nonNullTest: Z + get() = Z(global.x1 + this.x1 as UInt, global.x2 + this.x2 as Int) + set(value) { + global = Z(this.x1 as UInt + value.x1, this.x2 as Int + value.x2) + } + + var nullableTest: Z? + get() = Z(global.x1 + this.x1 as UInt, global.x2 + this.x2 as Int) + set(value) { + global = Z(this.x1 as UInt + value!!.x1, this.x2 as Int + value!!.x2) + } + +} + +fun box(): String { + val zZero = Z(0U, 0) + val zOne = Z(1U, -1) + val zTwo = Z(2U, -2) + val zThree = Z(3U, -3) + val zFour = Z(4U, -4) + + val sOne = S("1", "-1") + + val aOne = A(1U, -1) + + global = zZero + assertEquals(zOne, Z::nonNullTest.call(zOne)) + assertEquals(zOne, zOne::nonNullTest.call()) + assertEquals(zOne, Z::nonNullTest.getter.call(zOne)) + assertEquals(zOne, zOne::nonNullTest.getter.call()) + Z::nonNullTest.setter.call(zOne, zTwo) + assertEquals(zThree, global) + zOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, Z::nullableTest.call(zOne)) + assertEquals(zOne, zOne::nullableTest.call()) + assertEquals(zOne, Z::nullableTest.getter.call(zOne)) + assertEquals(zOne, zOne::nullableTest.getter.call()) + Z::nullableTest.setter.call(zOne, zTwo) + assertEquals(zThree, global) + zOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, S::nonNullTest.call(sOne)) + assertEquals(zOne, sOne::nonNullTest.call()) + assertEquals(zOne, S::nonNullTest.getter.call(sOne)) + assertEquals(zOne, sOne::nonNullTest.getter.call()) + S::nonNullTest.setter.call(sOne, zTwo) + assertEquals(zThree, global) + sOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, S::nullableTest.call(sOne)) + assertEquals(zOne, sOne::nullableTest.call()) + assertEquals(zOne, S::nullableTest.getter.call(sOne)) + assertEquals(zOne, sOne::nullableTest.getter.call()) + S::nullableTest.setter.call(sOne, zTwo) + assertEquals(zThree, global) + sOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, A::nonNullTest.call(aOne)) + assertEquals(zOne, aOne::nonNullTest.call()) + assertEquals(zOne, A::nonNullTest.getter.call(aOne)) + assertEquals(zOne, aOne::nonNullTest.getter.call()) + A::nonNullTest.setter.call(aOne, zTwo) + assertEquals(zThree, global) + aOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, A::nullableTest.call(aOne)) + assertEquals(zOne, aOne::nullableTest.call()) + assertEquals(zOne, A::nullableTest.getter.call(aOne)) + assertEquals(zOne, aOne::nullableTest.getter.call()) + A::nullableTest.setter.call(aOne, zTwo) + assertEquals(zThree, global) + aOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt new file mode 100644 index 00000000000..6dc55f7d51b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +interface ITest { + fun test(a: Int, b: Z, c: Z?): String +} + +@JvmInline +value class Z(val x1: UInt, val x2: Int) : ITest { + override fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +@JvmInline +value class S(val x1: String, val x2: String) : ITest { + override fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +@JvmInline +value class A(val x1: Any, val x2: Any) : ITest { + override fun test(a: Int, b: Z, c: Z?) = "$x1$x2$a${b.x1}${b.x2}${c!!.x1}${c!!.x2}" +} + +fun box(): String { + val two = Z(2U, 3) + val four = Z(4U, 5) + + assertEquals("0912345", Z::test.call(Z(0U, 9), 1, two, four)) + assertEquals("0912345", Z(0U, 9)::test.call(1, two, four)) + + assertEquals("0912345", S::test.call(S("0", "9"), 1, two, four)) + assertEquals("0912345", S("0", "9")::test.call(1, two, four)) + + assertEquals("0912345", A::test.call(A(0U, 9), 1, two, four)) + assertEquals("0912345", A(0U, 9)::test.call(1, two, four)) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt new file mode 100644 index 00000000000..44c811b6c31 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt @@ -0,0 +1,132 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +var global = Z(0U, 0) + +interface ITest { + var nonNullTest: Z + var nullableTest: Z? +} + +@JvmInline +value class Z(val x1: UInt, val x2: Int) : ITest { + override var nonNullTest: Z + get() = Z(global.x1 + this.x1, global.x2 + this.x2) + set(value) { + global = Z(this.x1 + value.x1, this.x2 + value.x2) + } + + override var nullableTest: Z? + get() = Z(global.x1 + this.x1, global.x2 + this.x2) + set(value) { + global = Z(this.x1 + value!!.x1, this.x2 + value!!.x2) + } +} + +@JvmInline +value class S(val x1: String, val x2: String) : ITest { + override var nonNullTest: Z + get() = Z(global.x1 + x1.toUInt(), global.x2 + x2.toInt()) + set(value) { + global = Z(this.x1.toUInt() + value.x1, this.x2.toInt() + value.x2) + } + + override var nullableTest: Z? + get() = Z(global.x1 + x1.toUInt(), global.x2 + x2.toInt()) + set(value) { + global = Z(this.x1.toUInt() + value!!.x1, this.x2.toInt() + value!!.x2) + } +} + +@JvmInline +value class A(val x1: Any, val x2: Any) : ITest { + override var nonNullTest: Z + get() = Z(global.x1 + this.x1 as UInt, global.x2 + this.x2 as Int) + set(value) { + global = Z(this.x1 as UInt + value.x1, this.x2 as Int + value.x2) + } + + override var nullableTest: Z? + get() = Z(global.x1 + this.x1 as UInt, global.x2 + this.x2 as Int) + set(value) { + global = Z(this.x1 as UInt + value!!.x1, this.x2 as Int + value!!.x2) + } + +} + +fun box(): String { + val zZero = Z(0U, 0) + val zOne = Z(1U, -1) + val zTwo = Z(2U, -2) + val zThree = Z(3U, -3) + val zFour = Z(4U, -4) + + val sOne = S("1", "-1") + + val aOne = A(1U, -1) + + global = zZero + assertEquals(zOne, Z::nonNullTest.call(zOne)) + assertEquals(zOne, zOne::nonNullTest.call()) + assertEquals(zOne, Z::nonNullTest.getter.call(zOne)) + assertEquals(zOne, zOne::nonNullTest.getter.call()) + Z::nonNullTest.setter.call(zOne, zTwo) + assertEquals(zThree, global) + zOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, Z::nullableTest.call(zOne)) + assertEquals(zOne, zOne::nullableTest.call()) + assertEquals(zOne, Z::nullableTest.getter.call(zOne)) + assertEquals(zOne, zOne::nullableTest.getter.call()) + Z::nullableTest.setter.call(zOne, zTwo) + assertEquals(zThree, global) + zOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, S::nonNullTest.call(sOne)) + assertEquals(zOne, sOne::nonNullTest.call()) + assertEquals(zOne, S::nonNullTest.getter.call(sOne)) + assertEquals(zOne, sOne::nonNullTest.getter.call()) + S::nonNullTest.setter.call(sOne, zTwo) + assertEquals(zThree, global) + sOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, S::nullableTest.call(sOne)) + assertEquals(zOne, sOne::nullableTest.call()) + assertEquals(zOne, S::nullableTest.getter.call(sOne)) + assertEquals(zOne, sOne::nullableTest.getter.call()) + S::nullableTest.setter.call(sOne, zTwo) + assertEquals(zThree, global) + sOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, A::nonNullTest.call(aOne)) + assertEquals(zOne, aOne::nonNullTest.call()) + assertEquals(zOne, A::nonNullTest.getter.call(aOne)) + assertEquals(zOne, aOne::nonNullTest.getter.call()) + A::nonNullTest.setter.call(aOne, zTwo) + assertEquals(zThree, global) + aOne::nonNullTest.setter.call(zThree) + assertEquals(zFour, global) + + global = zZero + assertEquals(zOne, A::nullableTest.call(aOne)) + assertEquals(zOne, aOne::nullableTest.call()) + assertEquals(zOne, A::nullableTest.getter.call(aOne)) + assertEquals(zOne, aOne::nullableTest.getter.call()) + A::nullableTest.setter.call(aOne, zTwo) + assertEquals(zThree, global) + aOne::nullableTest.setter.call(zThree) + assertEquals(zFour, global) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt new file mode 100644 index 00000000000..d25127df02d --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt @@ -0,0 +1,180 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +@JvmInline +value class Z(val x1: UInt, val x2: Int) { + var x3 + get() = x1 + set(value) = Unit +} +@JvmInline +value class Z2(val x1: Z, val x2: Z) { + var x3 + get() = x1 + set(value) = Unit +} + +@JvmInline +value class L(val x1: ULong, val x2: Long) { + var x3 + get() = x1 + set(value) = Unit +} +@JvmInline +value class L2(val x1: L, val x2: L) { + var x3 + get() = x1 + set(value) = Unit +} + +@JvmInline +value class A1(val x1: Any?, val x2: Any?) { + var x3 + get() = x1 + set(value) = Unit +} +@JvmInline +value class A1_2(val x1: A1, val x2: A1) { + var x3 + get() = x1 + set(value) = Unit +} +@JvmInline +value class A2(val x1: Any, val x2: Any) { + var x3 + get() = x1 + set(value) = Unit +} +@JvmInline +value class A2_2(val x1: A2, val x2: A2) { + var x3 + get() = x1 + set(value) = Unit +} + +fun box(): String { + assertEquals(42U, Z::x1.call(Z(42U, 43))) + assertEquals(43, Z::x2.call(Z(42U, 43))) + assertEquals(42U, Z::x3.call(Z(42U, 43))) + assertEquals(42U, Z::x3.getter.call(Z(42U, 43))) + assertEquals(Unit, Z::x3.setter.call(Z(42U, 43), 42U)) + + assertEquals(42U, Z(42U, 43)::x1.call()) + assertEquals(43, Z(42U, 43)::x2.call()) + assertEquals(42U, Z(42U, 43)::x3.call()) + assertEquals(42U, Z(42U, 43)::x3.getter.call()) + assertEquals(Unit, Z(42U, 43)::x3.setter.call(42U)) + + assertEquals(1234UL, L::x1.call(L(1234UL, 5678L))) + assertEquals(5678L, L::x2.call(L(1234UL, 5678L))) + assertEquals(1234UL, L::x3.call(L(1234UL, 5678L))) + assertEquals(1234UL, L::x3.getter.call(L(1234UL, 5678L))) + assertEquals(Unit, L::x3.setter.call(L(1234UL, 5678L), 1234UL)) + + assertEquals(1234UL, L(1234UL, 5678L)::x1.call()) + assertEquals(5678L, L(1234UL, 5678L)::x2.call()) + assertEquals(1234UL, L(1234UL, 5678L)::x3.call()) + assertEquals(1234UL, L(1234UL, 5678L)::x3.getter.call()) + assertEquals(Unit, L(1234UL, 5678L)::x3.setter.call(1234UL)) + + assertEquals("abc", A1::x1.call(A1("abc", "def"))) + assertEquals("def", A1::x2.call(A1("abc", "def"))) + assertEquals("abc", A1::x3.call(A1("abc", "def"))) + assertEquals("abc", A1::x3.getter.call(A1("abc", "def"))) + assertEquals(Unit, A1::x3.setter.call(A1("abc", "def"), "abc")) + + assertEquals("abc", A1("abc", "def")::x1.call()) + assertEquals("def", A1("abc", "def")::x2.call()) + assertEquals("abc", A1("abc", "def")::x3.call()) + assertEquals("abc", A1("abc", "def")::x3.getter.call()) + assertEquals(Unit, A1("abc", "def")::x3.setter.call("abc")) + + assertEquals(null, A1::x1.call(A1(null, null))) + assertEquals(null, A1::x2.call(A1(null, null))) + assertEquals(null, A1::x3.call(A1(null, null))) + assertEquals(null, A1::x3.getter.call(A1(null, null))) + assertEquals(Unit, A1::x3.setter.call(A1(null, null), null)) + + assertEquals(null, A1(null, null)::x1.call()) + assertEquals(null, A1(null, null)::x2.call()) + assertEquals(null, A1(null, null)::x3.call()) + assertEquals(null, A1(null, null)::x3.getter.call()) + assertEquals(Unit, A1(null, null)::x3.setter.call(null)) + + assertEquals("abc", A2::x1.call(A2("abc", "def"))) + assertEquals("def", A2::x2.call(A2("abc", "def"))) + assertEquals("abc", A2::x3.call(A2("abc", "def"))) + assertEquals("abc", A2::x3.getter.call(A2("abc", "def"))) + assertEquals(Unit, A2::x3.setter.call(A2("abc", "def"), "abc")) + + assertEquals("abc", A2("abc", "def")::x1.call()) + assertEquals("def", A2("abc", "def")::x2.call()) + assertEquals("abc", A2("abc", "def")::x3.call()) + assertEquals("abc", A2("abc", "def")::x3.getter.call()) + assertEquals(Unit, A2("abc", "def")::x3.setter.call("abc")) + + assertEquals(Z(42U, 43), Z2::x1.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Z(44U, 45), Z2::x2.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Z(42U, 43), Z2::x3.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Z(42U, 43), Z2::x3.getter.call(Z2(Z(42U, 43), Z(44U, 45)))) + assertEquals(Unit, Z2::x3.setter.call(Z2(Z(42U, 43), Z(44U, 45)), Z(42U, 43))) + + assertEquals(Z(42U, 43), Z2(Z(42U, 43), Z(44U, 45))::x1.call()) + assertEquals(Z(44U, 45), Z2(Z(42U, 43), Z(44U, 45))::x2.call()) + assertEquals(Z(42U, 43), Z2(Z(42U, 43), Z(44U, 45))::x3.call()) + assertEquals(Z(42U, 43), Z2(Z(42U, 43), Z(44U, 45))::x3.getter.call()) + assertEquals(Unit, Z2(Z(42U, 43), Z(44U, 45))::x3.setter.call(Z(42U, 43))) + + assertEquals(L(1234UL, 5678L), L2::x1.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(L(12340UL, -5678L), L2::x2.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(L(1234UL, 5678L), L2::x3.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(L(1234UL, 5678L), L2::x3.getter.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)))) + assertEquals(Unit, L2::x3.setter.call(L2(L(1234UL, 5678L), L(12340UL, -5678L)), L(1234UL, 5678L))) + + assertEquals(L(1234UL, 5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x1.call()) + assertEquals(L(12340UL, -5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x2.call()) + assertEquals(L(1234UL, 5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x3.call()) + assertEquals(L(1234UL, 5678L), L2(L(1234UL, 5678L), L(12340UL, -5678L))::x3.getter.call()) + assertEquals(Unit, L2(L(1234UL, 5678L), L(12340UL, -5678L))::x3.setter.call(L(1234UL, 5678L))) + + assertEquals(A1("abc", "def"), A1_2::x1.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(A1("geh", "ijk"), A1_2::x2.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(A1("abc", "def"), A1_2::x3.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(A1("abc", "def"), A1_2::x3.getter.call(A1_2(A1("abc", "def"), A1("geh", "ijk")))) + assertEquals(Unit, A1_2::x3.setter.call(A1_2(A1("abc", "def"), A1("geh", "ijk")), A1("abc", "def"))) + + assertEquals(A1("abc", "def"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x1.call()) + assertEquals(A1("geh", "ijk"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x2.call()) + assertEquals(A1("abc", "def"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x3.call()) + assertEquals(A1("abc", "def"), A1_2(A1("abc", "def"), A1("geh", "ijk"))::x3.getter.call()) + assertEquals(Unit, A1_2(A1("abc", "def"), A1("geh", "ijk"))::x3.setter.call(A1("abc", "def"))) + + assertEquals(A1(null, null), A1_2::x1.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(A1(null, null), A1_2::x2.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(A1(null, null), A1_2::x3.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(A1(null, null), A1_2::x3.getter.call(A1_2(A1(null, null), A1(null, null)))) + assertEquals(Unit, A1_2::x3.setter.call(A1_2(A1(null, null), A1(null, null)), A1(null, null))) + + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x1.call()) + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x2.call()) + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x3.call()) + assertEquals(A1(null, null), A1_2(A1(null, null), A1(null, null))::x3.getter.call()) + assertEquals(Unit, A1_2(A1(null, null), A1(null, null))::x3.setter.call(A1(null, null))) + + assertEquals(A2("abc", "def"), A2_2::x1.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(A2("geh", "ijk"), A2_2::x2.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(A2("abc", "def"), A2_2::x3.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(A2("abc", "def"), A2_2::x3.getter.call(A2_2(A2("abc", "def"), A2("geh", "ijk")))) + assertEquals(Unit, A2_2::x3.setter.call(A2_2(A2("abc", "def"), A2("geh", "ijk")), A2("abc", "def"))) + + assertEquals(A2("abc", "def"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x1.call()) + assertEquals(A2("geh", "ijk"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x2.call()) + assertEquals(A2("abc", "def"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x3.call()) + assertEquals(A2("abc", "def"), A2_2(A2("abc", "def"), A2("geh", "ijk"))::x3.getter.call()) + assertEquals(Unit, A2_2(A2("abc", "def"), A2("geh", "ijk"))::x3.setter.call(A2("abc", "def"))) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt new file mode 100644 index 00000000000..3e37c63126e --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt @@ -0,0 +1,122 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.KMutableProperty2 +import kotlin.test.assertEquals + +@JvmInline +value class Z(val value1: UInt, val value2: Int) { + operator fun plus(other: Z): Z = Z(this.value1 + other.value1, this.value2 + other.value2) +} + +class C { + var nonNullMember: Z = Z(0U, 0) + var nullableMember: Z? = Z(0U, 0) + + private var offset = Z(0U, 0) + var Z.nonNull_nonNullMemExt: Z + get() = this + offset + set(value) { offset = this + value } + + var Z.nonNull_nullableMemExt: Z? + get() = this + offset + set(value) { offset = this + value!! } + + var Z?.nullable_nonNullMemExt: Z + get() = this!! + offset + set(value) { offset = this!! + value } + + var Z?.nullable_nullableMemExt: Z? + get() = this!! + offset + set(value) { offset = this!! + value!! } +} + +var nonNullTopLevel: Z = Z(0U, 0) +var nullableTopLevel: Z? = Z(0U, 0) + +private var offset = Z(0U, 0) +var Z.nonNull_nonNullExt: Z + get() = this + offset + set(value) { offset = this + value } + +var Z.nonNull_nullableExt: Z? + get() = this + offset + set(value) { offset = this + value!! } + +var Z?.nullable_nonNullExt: Z + get() = this!! + offset + set(value) { offset = this!! + value } + +var Z?.nullable_nullableExt: Z? + get() = this!! + offset + set(value) { offset = this!! + value!! } + +fun box(): String { + val one = Z(1U, -1) + val two = Z(2U, -2) + val three = Z(3U, -3) + + val c = C() + assertEquals(Unit, C::nonNullMember.setter.call(c, one)) + assertEquals(one, C::nonNullMember.call(c)) + assertEquals(one, C::nonNullMember.getter.call(c)) + + assertEquals(Unit, c::nonNullMember.setter.call(two)) + assertEquals(two, c::nonNullMember.call()) + assertEquals(two, c::nonNullMember.getter.call()) + + assertEquals(Unit, C::nullableMember.setter.call(c, one)) + assertEquals(one, C::nullableMember.call(c)) + assertEquals(one, C::nullableMember.getter.call(c)) + + assertEquals(Unit, c::nullableMember.setter.call(two)) + assertEquals(two, c::nullableMember.call()) + assertEquals(two, c::nullableMember.getter.call()) + + val nonNull_nonNullMemExt = C::class.members.single { it.name == "nonNull_nonNullMemExt" } as KMutableProperty2 + assertEquals(Unit, nonNull_nonNullMemExt.setter.call(c, Z(0U, 0), two)) + assertEquals(three, nonNull_nonNullMemExt.call(c, one)) + assertEquals(three, nonNull_nonNullMemExt.getter.call(c, one)) + + val nonNull_nullableMemExt = C::class.members.single { it.name == "nonNull_nullableMemExt" } as KMutableProperty2 + assertEquals(Unit, nonNull_nullableMemExt.setter.call(c, Z(0U, 0), two)) + assertEquals(three, nonNull_nullableMemExt.call(c, one)) + assertEquals(three, nonNull_nullableMemExt.getter.call(c, one)) + + val nullable_nonNullMemExt = C::class.members.single { it.name == "nullable_nonNullMemExt" } as KMutableProperty2 + assertEquals(Unit, nullable_nonNullMemExt.setter.call(c, Z(0U, 0), two)) + assertEquals(three, nullable_nonNullMemExt.call(c, one)) + assertEquals(three, nullable_nonNullMemExt.getter.call(c, one)) + + val nullable_nullableMemExt = C::class.members.single { it.name == "nullable_nullableMemExt" } as KMutableProperty2 + assertEquals(Unit, nullable_nullableMemExt.setter.call(c, Z(0U, 0), two)) + assertEquals(three, nullable_nullableMemExt.call(c, one)) + assertEquals(three, nullable_nullableMemExt.getter.call(c, one)) + + assertEquals(Unit, ::nonNullTopLevel.setter.call(one)) + assertEquals(one, ::nonNullTopLevel.call()) + assertEquals(one, ::nonNullTopLevel.getter.call()) + + assertEquals(Unit, ::nullableTopLevel.setter.call(one)) + assertEquals(one, ::nullableTopLevel.call()) + assertEquals(one, ::nullableTopLevel.getter.call()) + + assertEquals(Unit, Z::nonNull_nonNullExt.setter.call(Z(0U, 0), two)) + assertEquals(three, Z::nonNull_nonNullExt.call(one)) + assertEquals(three, Z::nonNull_nonNullExt.getter.call(one)) + + assertEquals(Unit, Z::nonNull_nullableExt.setter.call(Z(0U, 0), two)) + assertEquals(three, Z::nonNull_nullableExt.call(one)) + assertEquals(three, Z::nonNull_nullableExt.getter.call(one)) + + assertEquals(Unit, Z?::nullable_nonNullExt.setter.call(Z(0U, 0), two)) + assertEquals(three, Z?::nullable_nonNullExt.call(one)) + assertEquals(three, Z?::nullable_nonNullExt.getter.call(one)) + + assertEquals(Unit, Z?::nullable_nullableExt.setter.call(Z(0U, 0), two)) + assertEquals(three, Z?::nullable_nullableExt.call(one)) + assertEquals(three, Z?::nullable_nullableExt.getter.call(one)) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt b/compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt new file mode 100644 index 00000000000..5b19e09fdd1 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt @@ -0,0 +1,91 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// WITH_COROUTINES +// LANGUAGE: +ValueClasses + +import kotlin.coroutines.startCoroutine +import kotlin.reflect.full.callSuspend +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import helpers.* + +@JvmInline +value class Z(val value1: UInt, val value2: Int) + +class C { + private var value: Z = Z(0U, 0) + + suspend fun nonNullConsume(z: Z) { value = z } + suspend fun nonNullProduce(): Z = value + suspend fun nullableConsume(z: Z?) { value = z!! } + suspend fun nullableProduce(): Z? = value + suspend fun nonNull_nonNullConsumeAndProduce(z: Z): Z = z + suspend fun nonNull_nullableConsumeAndProduce(z: Z): Z? = z + suspend fun nullable_nonNullConsumeAndProduce(z: Z?): Z = z!! + suspend fun nullable_nullableConsumeAndProduce(z: Z?): Z? = z +} + +private fun run0(f: suspend () -> Int): Int { + var result = -1 + f.startCoroutine(handleResultContinuation { result = it }) + return result +} + +private fun run0U(f: suspend () -> UInt): UInt { + var result = UInt.MAX_VALUE + f.startCoroutine(handleResultContinuation { result = it }) + return result +} + +fun box(): String { + val c = C() + + run0U { + C::nonNullConsume.callSuspend(c, Z(1U, -1)) + C::nonNullProduce.callSuspend(c).value1 + }.let { assertEquals(1U, it) } + + run0 { + C::nonNullConsume.callSuspend(c, Z(1U, -1)) + C::nonNullProduce.callSuspend(c).value2 + }.let { assertEquals(-1, it) } + + run0U { + C::nullableConsume.callSuspend(c, Z(2U, -2)) + C::nullableProduce.callSuspend(c)!!.value1 + }.let { assertEquals(2U, it) } + run0 { + C::nullableConsume.callSuspend(c, Z(2U, -2)) + C::nullableProduce.callSuspend(c)!!.value2 + }.let { assertEquals(-2, it) } + + run0U { + C::nonNull_nonNullConsumeAndProduce.callSuspend(c, Z(3U, -3)).value1 + }.let { assertEquals(3U, it) } + run0 { + C::nonNull_nonNullConsumeAndProduce.callSuspend(c, Z(3U, -3)).value2 + }.let { assertEquals(-3, it) } + + run0U { + C::nonNull_nullableConsumeAndProduce.callSuspend(c, Z(4U, -4))!!.value1 + }.let { assertEquals(4U, it) } + run0 { + C::nonNull_nullableConsumeAndProduce.callSuspend(c, Z(4U, -4))!!.value2 + }.let { assertEquals(-4, it) } + + run0U { + C::nullable_nonNullConsumeAndProduce.callSuspend(c, Z(5U, -5)).value1 + }.let { assertEquals(5U, it) } + run0 { + C::nullable_nonNullConsumeAndProduce.callSuspend(c, Z(5U, -5)).value2 + }.let { assertEquals(-5, it) } + + run0U { + C::nullable_nullableConsumeAndProduce.callSuspend(c, Z(6U, -6))!!.value1 + }.let { assertEquals(6U, it) } + run0 { + C::nullable_nullableConsumeAndProduce.callSuspend(c, Z(6U, -6))!!.value2 + }.let { assertEquals(-6, it) } + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt b/compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt new file mode 100644 index 00000000000..9de476a627b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt @@ -0,0 +1,72 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals + +@JvmInline +value class A(val x: UInt, val y: Int) + +fun test1(x: A = A(0U, 1)) = "OK" + +fun test32( + arg00: Long = 0L, arg01: Long = 0L, arg02: Long = 0L, arg03: Long = 0L, arg04: Long = 0L, + arg05: Long = 0L, arg06: Long = 0L, arg07: Long = 0L, arg08: Long = 0L, arg09: Long = 0L, + arg10: Long = 0L, arg11: Long = 0L, arg12: Long = 0L, arg13: Long = 0L, arg14: Long = 0L, + arg15: Long = 0L, arg16: Long = 0L, arg17: Long = 0L, arg18: Long = 0L, arg19: Long = 0L, + arg20: Long = 0L, arg21: Long = 0L, arg22: Long = 0L, arg23: Long = 0L, arg24: Long = 0L, + arg25: Long = 0L, arg26: Long = 0L, arg27: Long = 0L, arg28: Long = 0L, arg29: Long = 0L, + arg30: Long = 0L, x: A = A(0U, 1) +) = "OK" + +fun test33( + arg00: Long = 0L, arg01: Long = 0L, arg02: Long = 0L, arg03: Long = 0L, arg04: Long = 0L, + arg05: Long = 0L, arg06: Long = 0L, arg07: Long = 0L, arg08: Long = 0L, arg09: Long = 0L, + arg10: Long = 0L, arg11: Long = 0L, arg12: Long = 0L, arg13: Long = 0L, arg14: Long = 0L, + arg15: Long = 0L, arg16: Long = 0L, arg17: Long = 0L, arg18: Long = 0L, arg19: Long = 0L, + arg20: Long = 0L, arg21: Long = 0L, arg22: Long = 0L, arg23: Long = 0L, arg24: Long = 0L, + arg25: Long = 0L, arg26: Long = 0L, arg27: Long = 0L, arg28: Long = 0L, arg29: Long = 0L, + arg30: Long = 0L, arg31: Long = 0L, x: A = A(0U, 1) +) = "OK" + +fun test64( + arg00: Long = 0L, arg01: Long = 0L, arg02: Long = 0L, arg03: Long = 0L, arg04: Long = 0L, + arg05: Long = 0L, arg06: Long = 0L, arg07: Long = 0L, arg08: Long = 0L, arg09: Long = 0L, + arg10: Long = 0L, arg11: Long = 0L, arg12: Long = 0L, arg13: Long = 0L, arg14: Long = 0L, + arg15: Long = 0L, arg16: Long = 0L, arg17: Long = 0L, arg18: Long = 0L, arg19: Long = 0L, + arg20: Long = 0L, arg21: Long = 0L, arg22: Long = 0L, arg23: Long = 0L, arg24: Long = 0L, + arg25: Long = 0L, arg26: Long = 0L, arg27: Long = 0L, arg28: Long = 0L, arg29: Long = 0L, + arg30: Long = 0L, arg31: Long = 0L, arg32: Long = 0L, arg33: Long = 0L, arg34: Long = 0L, + arg35: Long = 0L, arg36: Long = 0L, arg37: Long = 0L, arg38: Long = 0L, arg39: Long = 0L, + arg40: Long = 0L, arg41: Long = 0L, arg42: Long = 0L, arg43: Long = 0L, arg44: Long = 0L, + arg45: Long = 0L, arg46: Long = 0L, arg47: Long = 0L, arg48: Long = 0L, arg49: Long = 0L, + arg50: Long = 0L, arg51: Long = 0L, arg52: Long = 0L, arg53: Long = 0L, arg54: Long = 0L, + arg55: Long = 0L, arg56: Long = 0L, arg57: Long = 0L, arg58: Long = 0L, arg59: Long = 0L, + arg60: Long = 0L, arg61: Long = 0L, arg62: Long = 0L, x: A = A(0U, 1) +) = "OK" + +fun test65( + arg00: Long = 0L, arg01: Long = 0L, arg02: Long = 0L, arg03: Long = 0L, arg04: Long = 0L, + arg05: Long = 0L, arg06: Long = 0L, arg07: Long = 0L, arg08: Long = 0L, arg09: Long = 0L, + arg10: Long = 0L, arg11: Long = 0L, arg12: Long = 0L, arg13: Long = 0L, arg14: Long = 0L, + arg15: Long = 0L, arg16: Long = 0L, arg17: Long = 0L, arg18: Long = 0L, arg19: Long = 0L, + arg20: Long = 0L, arg21: Long = 0L, arg22: Long = 0L, arg23: Long = 0L, arg24: Long = 0L, + arg25: Long = 0L, arg26: Long = 0L, arg27: Long = 0L, arg28: Long = 0L, arg29: Long = 0L, + arg30: Long = 0L, arg31: Long = 0L, arg32: Long = 0L, arg33: Long = 0L, arg34: Long = 0L, + arg35: Long = 0L, arg36: Long = 0L, arg37: Long = 0L, arg38: Long = 0L, arg39: Long = 0L, + arg40: Long = 0L, arg41: Long = 0L, arg42: Long = 0L, arg43: Long = 0L, arg44: Long = 0L, + arg45: Long = 0L, arg46: Long = 0L, arg47: Long = 0L, arg48: Long = 0L, arg49: Long = 0L, + arg50: Long = 0L, arg51: Long = 0L, arg52: Long = 0L, arg53: Long = 0L, arg54: Long = 0L, + arg55: Long = 0L, arg56: Long = 0L, arg57: Long = 0L, arg58: Long = 0L, arg59: Long = 0L, + arg60: Long = 0L, arg61: Long = 0L, arg62: Long = 0L, arg63: Long = 0L, x: A = A(0U, 1) +) = "OK" + +fun box(): String { + assertEquals("OK", ::test1.callBy(mapOf())) + assertEquals("OK", ::test32.callBy(mapOf())) + assertEquals("OK", ::test33.callBy(mapOf())) + assertEquals("OK", ::test64.callBy(mapOf())) + assertEquals("OK", ::test65.callBy(mapOf())) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt b/compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt new file mode 100644 index 00000000000..f84056ac255 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt @@ -0,0 +1,53 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@JvmInline +value class S(val value1: UInt, val value2: String) { + operator fun plus(other: S): S = S(this.value1 * 10U + other.value1, this.value2 + other.value2) +} + +class C { + fun member(a: S, b: S = S(1U, "b2")): S = a + b +} + +fun topLevel(c: S, d: S = S(1U, "d2")): S = c + d + +class D(e: S, f: S = S(1U, "f2")) { + val result = e + f +} + +fun S.extension(h: S = S(1U, "h2")): S = this + h + +fun box(): String { + assertEquals(S(11U, "a2b2"), C().member(S(1U, "a2"))) + assertEquals( + S(11U, "a2b2"), + C::member.callBy(C::member.parameters.filter { it.name != "b" }.associateWith { (if (it.name == "a") S(1U, "a2") else C()) }) + ) + + assertEquals(S(11U, "c2d2"), topLevel(S(1U, "c2"))) + assertEquals(S(11U, "c2d2"), ::topLevel.callBy(::topLevel.parameters.filter { it.name != "d" }.associateWith { S(1U, "c2") })) + + assertEquals(S(11U, "e2f2"), ::D.callBy(::D.parameters.filter { it.name != "f" }.associateWith { S(1U, "e2") }).result) + + assertEquals(S(11U, "g2h2"), S(1U, "g2").extension()) + assertEquals(S(11U, "g2h2"), S::extension.callBy(S::extension.parameters.filter { it.name != "h" }.associateWith { S(1U, "g2") })) + + val boundMember = C()::member + assertEquals(S(11U, "a2b2"), boundMember.callBy(boundMember.parameters.associateWith { S(1U, it.name!! + "2") })) + + val boundExtension = S(1U, "g2")::extension + assertEquals(S(11U, "g2h2"), boundExtension.callBy(boundExtension.parameters.associateWith { S(1U, it.name!! + "2") })) + + val mfvcConstructor = ::S + val exception = runCatching { mfvcConstructor.callBy(mapOf(mfvcConstructor.parameters.first() to 1U)) }.exceptionOrNull()!! + assertTrue(exception is IllegalArgumentException) + assertTrue(exception.message!!.startsWith("No argument provided for a required parameter: parameter #1 value2 of fun ``(kotlin.UInt, kotlin.String): "), exception.message) + assertTrue(exception.message!!.endsWith("S"), exception.message) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt b/compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt new file mode 100644 index 00000000000..6fb4129595b --- /dev/null +++ b/compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt @@ -0,0 +1,77 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses +import kotlin.test.assertEquals + +interface IFoo { + fun fooFun(z: Z): Z + var fooVar: Z +} + +var global = Z(0U, 0) + + +@JvmInline +value class Z(val x1: UInt, val x2: Int) : IFoo { + + override fun fooFun(z: Z): Z = Z(z.x1 + x1, z.x2 + x2) + + override var fooVar: Z + get() = Z(global.x1 + x1, global.x2 + x2) + set(value) { + global = Z(value.x1 + x1, value.x2 + x2) + } + + fun barFun(z: Z): Z = Z(z.x1 * 100U + x1, z.x2 * 100 + x2) + + var barVar: Z + get() = Z(global.x1 * 100U + x1, global.x2 * 100 + x2) + set(value) { + global = Z(value.x1 * 100U + x1, value.x2 * 100 + x2) + } +} + + +fun box(): String { + val fooFunR = Z::fooFun + assertEquals(Z(53U, -53), fooFunR.callBy(mapOf(fooFunR.parameters[0] to Z(42U, -42), fooFunR.parameters[1] to Z(11U, -11)))) + + val fooFunBR = Z(42U, -42)::fooFun + assertEquals(Z(142U, -142), fooFunBR.callBy(mapOf(fooFunBR.parameters[0] to Z(100U, -100)))) + + global = Z(0U, 0) + val fooVarR = Z::fooVar + assertEquals(Z(42U, -42), fooVarR.callBy(mapOf(fooVarR.parameters[0] to Z(42U, -42)))) + assertEquals(Z(42U, -42), fooVarR.getter.callBy(mapOf(fooVarR.getter.parameters[0] to Z(42U, -42)))) + fooVarR.setter.callBy(mapOf(fooVarR.setter.parameters[0] to Z(42U, -42), fooVarR.setter.parameters[1] to Z(1U, -1))) + assertEquals(Z(43U, -43), global) + + global = Z(100U, -100) + val fooVarBR = Z(42U, -42)::fooVar + assertEquals(Z(142U, -142), fooVarBR.callBy(mapOf())) + assertEquals(Z(142U, -142), fooVarBR.getter.callBy(mapOf())) + fooVarBR.setter.callBy(mapOf(fooVarBR.setter.parameters[0] to Z(1U, -1))) + assertEquals(Z(43U, -43), global) + + val barFunR = Z::barFun + assertEquals(Z(1142U, -1142), barFunR.callBy(mapOf(barFunR.parameters[0] to Z(42U, -42), barFunR.parameters[1] to Z(11U, -11)))) + + val barFunBR = Z(42U, -42)::barFun + assertEquals(Z(2242U, -2242), barFunBR.callBy(mapOf(barFunBR.parameters[0] to Z(22U, -22)))) + + global = Z(1U, -1) + val barVarR = Z::barVar + assertEquals(Z(142U, -142), barVarR.callBy(mapOf(barVarR.parameters[0] to Z(42U, -42)))) + assertEquals(Z(142U, -142), barVarR.getter.callBy(mapOf(barVarR.getter.parameters[0] to Z(42U, -42)))) + barVarR.setter.callBy(mapOf(barVarR.setter.parameters[0] to Z(42U, -42), barVarR.setter.parameters[1] to Z(3U, -3))) + assertEquals(Z(342U, -342), global) + + global = Z(2U, -2) + val barVarBR = Z(42U, -42)::barVar + assertEquals(Z(242U, -242), barVarBR.callBy(mapOf())) + assertEquals(Z(242U, -242), barVarBR.getter.callBy(mapOf())) + barVarBR.setter.callBy(mapOf(barVarBR.setter.parameters[0] to Z(4U, -4))) + assertEquals(Z(442U, -442), global) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt new file mode 100644 index 00000000000..7256f4002e7 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt @@ -0,0 +1,14 @@ +// LAMBDAS: CLASS +// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.jvm.reflect + +@JvmInline +value class C(val x1: UInt, val x2: Int) + +fun C.f(x: (String) -> Unit = { OK: String -> }) = x.reflect()?.parameters?.singleOrNull()?.name + +fun box(): String = C(0U, 1).f() ?: "null" diff --git a/compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt b/compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt new file mode 100644 index 00000000000..cfefc5376d2 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt @@ -0,0 +1,23 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.javaConstructor +import kotlin.reflect.jvm.kotlinFunction +import kotlin.test.assertEquals + +@JvmInline +value class Z(val x1: UInt, val x2: Int) + +class Test(val x: Z) + +fun box(): String { + val kctor1 = Test::class.primaryConstructor ?: throw AssertionError("No primary constructor") + val jctor1 = kctor1.javaConstructor ?: throw AssertionError("No javaConstructor for $kctor1") + val kctor2 = jctor1.kotlinFunction ?: throw AssertionError("No kotlinFunction for $jctor1") + + assertEquals(kctor1, kctor2) + assertEquals("[x]", kctor2.parameters.map { it.name }.toString()) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt b/compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt index d9f90b9d4d7..eb9ddc04dd0 100644 --- a/compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt +++ b/compiler/testData/codegen/box/reflection/mapping/types/inlineClassInSignature.kt @@ -8,20 +8,16 @@ inline class S(val value: String) fun S.foo(x: Int, s: S): S = this -/* TODO: Support calling members of inline classes in reflection (KT-26748) inline class T(val s: S) { fun bar(u: S): T = this } -*/ fun box(): String { assertEquals(listOf(String::class.java, Int::class.java, String::class.java), S::foo.parameters.map { it.type.javaType }) assertEquals(String::class.java, S::foo.returnType.javaType) -/* - assertEquals(listOf(), T::bar.parameters.map { it.type.javaType }) + assertEquals(listOf(String::class.java, String::class.java), T::bar.parameters.map { it.type.javaType }) assertEquals(String::class.java, T::bar.returnType.javaType) -*/ return "OK" } diff --git a/compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt b/compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt new file mode 100644 index 00000000000..e7b0a611346 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt @@ -0,0 +1,46 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals +import java.lang.reflect.Type + +@JvmInline +value class S(val value1: UInt, val value2: String) + +fun S.foo(x: Int, s: S): S = this + +fun compoundType(vararg types: Type) = listOf(*types).toString() + +@JvmInline +value class T(val s: S) { + fun bar(u: S): T = this +} +data class U(val s: S) { + fun bar(u: S): U = this +} + +@kotlin.ExperimentalStdlibApi +fun box(): String { + assertEquals(listOf(Int::class.java, String::class.java), ::S.parameters.map { it.type.javaType }) + assertEquals(S::class.java, ::S.returnType.javaType) + + assertEquals(listOf(compoundType(Int::class.java, String::class.java)), ::T.parameters.map { it.type.javaType.toString() }) + assertEquals(T::class.java, ::T.returnType.javaType) + + assertEquals(listOf(compoundType(Int::class.java, String::class.java)), ::U.parameters.map { it.type.javaType.toString() }) + assertEquals(U::class.java, ::U.returnType.javaType) + + + assertEquals(listOf(compoundType(Int::class.java, String::class.java), Int::class.java.toString(), compoundType(Int::class.java, String::class.java)), S::foo.parameters.map { it.type.javaType.toString() }) + assertEquals(S::class.java, S::foo.returnType.javaType) + + assertEquals(listOf(compoundType(Int::class.java, String::class.java), compoundType(Int::class.java, String::class.java)), T::bar.parameters.map { it.type.javaType.toString() }) + assertEquals(T::class.java, T::bar.returnType.javaType) + + assertEquals(listOf(U::class.java.toString(), compoundType(Int::class.java, String::class.java)), U::bar.parameters.map { it.type.javaType.toString() }) + assertEquals(U::class.java, U::bar.returnType.javaType) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt b/compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt new file mode 100644 index 00000000000..154089b498c --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt @@ -0,0 +1,85 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +package test + +import kotlin.reflect.KCallable +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals + +@JvmInline +value class Z1(val publicX1: UInt, val publicX2: Int) { + companion object { + val publicX1Ref = Z1::publicX1 + val publicX2Ref = Z1::publicX2 + val publicX1BoundRef = Z1(42U, 43)::publicX1 + val publicX2BoundRef = Z1(42U, 43)::publicX2 + } +} + +@JvmInline +value class Z2(internal val internalX1: UInt, internal val internalX2: Int) { + companion object { + val internalX1Ref = Z2::internalX1 + val internalX2Ref = Z2::internalX2 + val internalX1BoundRef = Z2(42U, 43)::internalX1 + val internalX2BoundRef = Z2(42U, 43)::internalX2 + } +} + +@JvmInline +value class Z3(private val privateX1: UInt, private val privateX2: Int) { + companion object { + val privateX1Ref = Z3::privateX1 + val privateX2Ref = Z3::privateX2 + val privateX1BoundRef = Z3(42U, 43)::privateX1 + val privateX2BoundRef = Z3(42U, 43)::privateX2 + } +} + +@JvmInline +value class ZZ(val x1: Z1, val x2: Z1) + +fun KCallable<*>.getJavaTypesOfParams() = parameters.map { it.type.javaType }.toString() +fun KCallable<*>.getJavaTypeOfResult() = returnType.javaType.toString() + +fun box(): String { + assertEquals("[class test.Z1]", Z1.publicX1Ref.getJavaTypesOfParams()) + assertEquals("[class test.Z1]", Z1.publicX2Ref.getJavaTypesOfParams()) + assertEquals("int", Z1.publicX1Ref.getJavaTypeOfResult()) + assertEquals("int", Z1.publicX2Ref.getJavaTypeOfResult()) + + assertEquals("[]", Z1.publicX1BoundRef.getJavaTypesOfParams()) + assertEquals("[]", Z1.publicX2BoundRef.getJavaTypesOfParams()) + assertEquals("int", Z1.publicX1BoundRef.getJavaTypeOfResult()) + assertEquals("int", Z1.publicX2BoundRef.getJavaTypeOfResult()) + + assertEquals("[class test.Z2]", Z2.internalX1Ref.getJavaTypesOfParams()) + assertEquals("[class test.Z2]", Z2.internalX2Ref.getJavaTypesOfParams()) + assertEquals("int", Z2.internalX1Ref.getJavaTypeOfResult()) + assertEquals("int", Z2.internalX2Ref.getJavaTypeOfResult()) + + assertEquals("[]", Z2.internalX1BoundRef.getJavaTypesOfParams()) + assertEquals("[]", Z2.internalX2BoundRef.getJavaTypesOfParams()) + assertEquals("int", Z2.internalX1BoundRef.getJavaTypeOfResult()) + assertEquals("int", Z2.internalX2BoundRef.getJavaTypeOfResult()) + + assertEquals("[class test.Z3]", Z3.privateX1Ref.getJavaTypesOfParams()) + assertEquals("[class test.Z3]", Z3.privateX2Ref.getJavaTypesOfParams()) + assertEquals("int", Z3.privateX1Ref.getJavaTypeOfResult()) + assertEquals("int", Z3.privateX2Ref.getJavaTypeOfResult()) + + assertEquals("[]", Z3.privateX1BoundRef.getJavaTypesOfParams()) + assertEquals("[]", Z3.privateX2BoundRef.getJavaTypesOfParams()) + assertEquals("int", Z3.privateX1BoundRef.getJavaTypeOfResult()) + assertEquals("int", Z3.privateX2BoundRef.getJavaTypeOfResult()) + + + assertEquals("[class test.ZZ]", ZZ::x1.getJavaTypesOfParams()) + assertEquals("[class test.ZZ]", ZZ::x2.getJavaTypesOfParams()) + assertEquals("class test.Z1", ZZ::x1.getJavaTypeOfResult()) + assertEquals("class test.Z1", ZZ::x2.getJavaTypeOfResult()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt b/compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt new file mode 100644 index 00000000000..e90367745ee --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt @@ -0,0 +1,90 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@JvmInline +value class Z1(val publicX1: UInt, val publicX2: Int) { + companion object { + val publicX1Ref = Z1::publicX1 + val publicX2Ref = Z1::publicX2 + val publicX1BoundRef = Z1(42U, -42)::publicX1 + val publicX2BoundRef = Z1(42U, -42)::publicX2 + } +} + +@JvmInline +value class Z2(internal val internalX1: UInt, internal val internalX2: Int) { + companion object { + val internalX1Ref = Z2::internalX1 + val internalX2Ref = Z2::internalX2 + val internalX1BoundRef = Z2(42U, -42)::internalX1 + val internalX2BoundRef = Z2(42U, -42)::internalX2 + } +} + +@JvmInline +value class Z3(private val privateX1: UInt, private val privateX2: Int) { + companion object { + val privateX1Ref = Z3::privateX1 + val privateX2Ref = Z3::privateX2 + val privateX1BoundRef = Z3(42U, -42)::privateX1 + val privateX2BoundRef = Z3(42U, -42)::privateX2 + } +} +@JvmInline +value class Z1_2(val publicX: Z1) { + companion object { + val publicXRef = Z1_2::publicX + val publicXBoundRef = Z1_2(Z1(42U, -42))::publicX + } +} + +@JvmInline +value class Z2_2(internal val internalX: Z2) { + companion object { + val internalXRef = Z2_2::internalX + val internalXBoundRef = Z2_2(Z2(42U, -42))::internalX + } +} + +@JvmInline +value class Z3_2(private val privateX: Z3) { + companion object { + val privateXRef = Z3_2::privateX + val privateXBoundRef = Z3_2(Z3(42U, -42))::privateX + } +} + +fun box(): String { + val suffix = "-pVg5ArA" + assertEquals("getPublicX1$suffix", Z1.publicX1Ref.javaGetter!!.name) + assertEquals("getPublicX2", Z1.publicX2Ref.javaGetter!!.name) + assertEquals("getPublicX1$suffix", Z1.publicX1BoundRef.javaGetter!!.name) + assertEquals("getPublicX2", Z1.publicX2BoundRef.javaGetter!!.name) + + assertTrue(Z2.internalX1Ref.javaGetter!!.name.startsWith("getInternalX1$suffix\$"), Z2.internalX1Ref.javaGetter!!.name) + assertTrue(Z2.internalX2Ref.javaGetter!!.name.startsWith("getInternalX2\$"), Z2.internalX2Ref.javaGetter!!.name) + assertTrue(Z2.internalX1BoundRef.javaGetter!!.name.startsWith("getInternalX1$suffix\$"), Z2.internalX1BoundRef.javaGetter!!.name) + assertTrue(Z2.internalX2BoundRef.javaGetter!!.name.startsWith("getInternalX2\$"), Z2.internalX2BoundRef.javaGetter!!.name) + + assertEquals(null, Z3.privateX1Ref.javaGetter) + assertEquals(null, Z3.privateX2Ref.javaGetter) + assertEquals(null, Z3.privateX1BoundRef.javaGetter) + assertEquals(null, Z3.privateX2BoundRef.javaGetter) + + + assertEquals("getPublicX", Z1_2.publicXRef.javaGetter!!.name) + assertEquals("getPublicX", Z1_2.publicXBoundRef.javaGetter!!.name) + + assertTrue(Z2_2.internalXRef.javaGetter!!.name.startsWith("getInternalX\$"), Z2_2.internalXRef.javaGetter!!.name) + assertTrue(Z2_2.internalXBoundRef.javaGetter!!.name.startsWith("getInternalX\$"), Z2_2.internalXBoundRef.javaGetter!!.name) + + assertEquals("getPrivateX", Z3_2.privateXRef.javaGetter!!.name) + assertEquals("getPrivateX", Z3_2.privateXBoundRef.javaGetter!!.name) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt b/compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt new file mode 100644 index 00000000000..293076abdaa --- /dev/null +++ b/compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt @@ -0,0 +1,49 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +package test + +import kotlin.reflect.* +import kotlin.reflect.jvm.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@JvmInline +value class Z(val value1: UInt, val value2: String) + +class S { + suspend fun consumeZ(z: Z) {} + suspend fun produceZ(): Z = Z(0U, "") + suspend fun consumeAndProduceZ(z: Z): Z = z +} + +fun box(): String { + val members = S::class.members.filterIsInstance>().associateBy(KFunction<*>::name) + + members["consumeZ"]!!.let { cz -> + val czj = cz.javaMethod!! + assertTrue(czj.name.startsWith("consumeZ-"), czj.name) + assertEquals("int, java.lang.String, kotlin.coroutines.Continuation", czj.parameterTypes.joinToString { it.name }) + val czjk = czj.kotlinFunction + assertEquals(cz, czjk) + } + + members["produceZ"]!!.let { pz -> + val pzj = pz.javaMethod!! + assertEquals("produceZ", pzj.name) + assertEquals("kotlin.coroutines.Continuation", pzj.parameterTypes.joinToString { it.name }) + val pzjk = pzj!!.kotlinFunction + assertEquals(pz, pzjk) + } + + members["consumeAndProduceZ"]!!.let { cpz -> + val cpzj = cpz.javaMethod!! + assertTrue(cpzj.name.startsWith("consumeAndProduceZ-"), cpzj.name) + assertEquals("int, java.lang.String, kotlin.coroutines.Continuation", cpzj.parameterTypes.joinToString { it.name }) + val cpzjk = cpzj!!.kotlinFunction + assertEquals(cpz, cpzjk) + } + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/modifiers/mfvc.kt b/compiler/testData/codegen/box/reflection/modifiers/mfvc.kt new file mode 100644 index 00000000000..77aae13ac16 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/modifiers/mfvc.kt @@ -0,0 +1,20 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses +// FILE: box.kt + +import kotlin.test.assertTrue +import kotlin.test.assertFalse +@JvmInline +value class V(val value: String, val value1: String) + +fun box(): String { + assertFalse(V::class.isSealed) + assertFalse(V::class.isData) + assertFalse(V::class.isInner) + assertFalse(V::class.isCompanion) + assertFalse(V::class.isFun) + assertTrue(V::class.isValue) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/mfvc.kt b/compiler/testData/codegen/box/reflection/typeOf/mfvc.kt new file mode 100644 index 00000000000..e58b1792f72 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/mfvc.kt @@ -0,0 +1,25 @@ +// TARGET_BACKEND: JVM_IR +// WITH_REFLECT +// LANGUAGE: +ValueClasses + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +@JvmInline +value class Z(val value1: String, val value2: String) + +fun check(expected: String, actual: KType) { + assertEquals(expected, actual.toString()) +} + +fun box(): String { + check("test.Z", typeOf()) + check("test.Z?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt new file mode 100644 index 00000000000..3fcf9916c46 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt @@ -0,0 +1,25 @@ +// TARGET_BACKEND: JVM_IR +// WITH_STDLIB +// LANGUAGE: +ValueClasses + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +@JvmInline +value class Z(val value1: String, val value2: String) + +fun check(expected: String, actual: KType) { + assertEquals(expected + " (Kotlin reflection is not available)", actual.toString()) +} + +fun box(): String { + check("test.Z", typeOf()) + check("test.Z?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + + return "OK" +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 800237d1d98..f7b3397dc62 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -41385,6 +41385,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @Nested @@ -42739,6 +42749,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/reflection/mapping/types/withNullability.kt"); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @Nested diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 08b2222a395..59ed6274ef1 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -43521,6 +43521,100 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/constructorWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("fieldAccessors.kt") + public void testFieldAccessors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/fieldAccessors.kt"); + } + + @Test + @TestMetadata("functionsWithMfvcParameters.kt") + public void testFunctionsWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/functionsWithMfvcParameters.kt"); + } + + @Test + @TestMetadata("internalPrimaryValOfMfvc.kt") + public void testInternalPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/internalPrimaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("jvmStaticFieldInObject.kt") + public void testJvmStaticFieldInObject() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFieldInObject.kt"); + } + + @Test + @TestMetadata("jvmStaticFunction.kt") + public void testJvmStaticFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/jvmStaticFunction.kt"); + } + + @Test + @TestMetadata("mfvcConstructor.kt") + public void testMfvcConstructor() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/mfvcConstructor.kt"); + } + + @Test + @TestMetadata("nonOverridingFunOfMfvc.kt") + public void testNonOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("nonOverridingVarOfMfvc.kt") + public void testNonOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/nonOverridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingFunOfMfvc.kt") + public void testOverridingFunOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingFunOfMfvc.kt"); + } + + @Test + @TestMetadata("overridingVarOfMfvc.kt") + public void testOverridingVarOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/overridingVarOfMfvc.kt"); + } + + @Test + @TestMetadata("primaryValOfMfvc.kt") + public void testPrimaryValOfMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/primaryValOfMfvc.kt"); + } + + @Test + @TestMetadata("properties.kt") + public void testProperties() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/properties.kt"); + } + + @Test + @TestMetadata("suspendFunction.kt") + public void testSuspendFunction() throws Exception { + runTest("compiler/testData/codegen/box/reflection/call/valueClasses/suspendFunction.kt"); + } + } } @Nested @@ -43640,6 +43734,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/callBy/manyMaskArguments.kt"); } + @Test + @TestMetadata("mfvcDefaultArguments.kt") + public void testMfvcDefaultArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcDefaultArguments.kt"); + } + + @Test + @TestMetadata("mfvcFunctionsAndConstructors.kt") + public void testMfvcFunctionsAndConstructors() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcFunctionsAndConstructors.kt"); + } + + @Test + @TestMetadata("mfvcMembers.kt") + public void testMfvcMembers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/callBy/mfvcMembers.kt"); + } + @Test @TestMetadata("nonDefaultParameterOmitted.kt") public void testNonDefaultParameterOmitted() throws Exception { @@ -44484,6 +44596,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt"); } + @Test + @TestMetadata("reflectOnDefaultWithMfvcArgument.kt") + public void testReflectOnDefaultWithMfvcArgument() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithMfvcArgument.kt"); + } + @Test @TestMetadata("reflectOnLambdaInArrayConstructor.kt") public void testReflectOnLambdaInArrayConstructor() throws Exception { @@ -44554,6 +44672,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithInlineClassParameters.kt"); } + @Test + @TestMetadata("constructorWithMfvcParameters.kt") + public void testConstructorWithMfvcParameters() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/constructorWithMfvcParameters.kt"); + } + @Test @TestMetadata("extensionProperty.kt") public void testExtensionProperty() throws Exception { @@ -44815,6 +44939,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/mapping/types/memberFunctions.kt"); } + @Test + @TestMetadata("mfvcInSignature.kt") + public void testMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcInSignature.kt"); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/types/mfvcPrimaryVal.kt"); + } + @Test @TestMetadata("overrideAnyWithPrimitive.kt") public void testOverrideAnyWithPrimitive() throws Exception { @@ -44881,6 +45017,28 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/mapping/types/withNullability.kt"); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("mfvcPrimaryVal.kt") + public void testMfvcPrimaryVal() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/mfvcPrimaryVal.kt"); + } + + @Test + @TestMetadata("suspendFunctionWithMfvcInSignature.kt") + public void testSuspendFunctionWithMfvcInSignature() throws Exception { + runTest("compiler/testData/codegen/box/reflection/mapping/valueClasses/suspendFunctionWithMfvcInSignature.kt"); + } + } } @Nested @@ -45094,6 +45252,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/modifiers/javaVisibility.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/modifiers/mfvc.kt"); + } + @Test @TestMetadata("properties.kt") public void testProperties() throws Exception { @@ -45904,6 +46068,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/mfvc.kt"); + } + @Test @TestMetadata("multipleLayers.kt") public void testMultipleLayers() throws Exception { @@ -46013,6 +46183,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); } + @Test + @TestMetadata("mfvc.kt") + public void testMfvc() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/mfvc.kt"); + } + @Test @TestMetadata("mutableCollections_after.kt") public void testMutableCollections_after() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 20ebb557773..f7ace6dc07f 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -33110,6 +33110,19 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } } + + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/reflection/callBy") @@ -34313,6 +34326,19 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/reflection/mapping/types/withNullability.kt"); } } + + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/reflection/methodsFromAny") diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index 40ca3fea147..ef0baa2ba94 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.inlineClassRepresentation +import org.jetbrains.kotlin.resolve.descriptorUtil.multiFieldValueClassRepresentation import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeUtils @@ -42,6 +43,8 @@ fun KotlinType.unsubstitutedUnderlyingTypes(): List { fun KotlinType.isInlineClassType(): Boolean = constructor.declarationDescriptor?.isInlineClass() ?: false +fun KotlinType.isMultiFieldValueClassType(): Boolean = constructor.declarationDescriptor?.isMultiFieldValueClass() ?: false +fun KotlinType.isValueClassType(): Boolean = constructor.declarationDescriptor?.isValueClass() ?: false fun KotlinType.needsMfvcFlattening(): Boolean = constructor.declarationDescriptor?.run { isMultiFieldValueClass() && !isNullableType() } == true @@ -77,6 +80,20 @@ fun KotlinType.isNullableUnderlyingType(): Boolean { fun CallableDescriptor.isGetterOfUnderlyingPropertyOfInlineClass() = this is PropertyGetterDescriptor && correspondingProperty.isUnderlyingPropertyOfInlineClass() +fun CallableDescriptor.isGetterOfUnderlyingPropertyOfMultiFieldValueClass() = + this is PropertyGetterDescriptor && correspondingProperty.isUnderlyingPropertyOfMultiFieldValueClass() + +fun CallableDescriptor.isGetterOfUnderlyingPropertyOfValueClass() = + this is PropertyGetterDescriptor && correspondingProperty.isUnderlyingPropertyOfValueClass() + fun VariableDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean = extensionReceiverParameter == null && (containingDeclaration as? ClassDescriptor)?.inlineClassRepresentation?.underlyingPropertyName == this.name + +fun VariableDescriptor.isUnderlyingPropertyOfMultiFieldValueClass(): Boolean = + extensionReceiverParameter == null && + (containingDeclaration as? ClassDescriptor)?.multiFieldValueClassRepresentation?.containsPropertyWithName(this.name) == true + +fun VariableDescriptor.isUnderlyingPropertyOfValueClass(): Boolean = + extensionReceiverParameter == null && + (containingDeclaration as? ClassDescriptor)?.valueClassRepresentation?.containsPropertyWithName(this.name) == true diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt index 9763c6f8985..a6162dc5a1e 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KCallableImpl.kt @@ -8,13 +8,14 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor +import org.jetbrains.kotlin.types.asSimpleType import java.lang.reflect.ParameterizedType import java.lang.reflect.Type import java.lang.reflect.WildcardType -import java.util.* import kotlin.coroutines.Continuation import kotlin.reflect.* import kotlin.reflect.jvm.internal.calls.Caller +import kotlin.reflect.jvm.internal.calls.getMfvcUnboxMethods import kotlin.reflect.jvm.javaType import kotlin.reflect.jvm.jvmErasure import java.lang.reflect.Array as ReflectArray @@ -113,7 +114,9 @@ internal abstract class KCallableImpl : KCallable, KTypeParameterOwner private val _absentArguments = ReflectProperties.lazySoft { val parameterSize = parameters.size + (if (isSuspend) 1 else 0) - val maskSize = (parameters.size + Integer.SIZE - 1) / Integer.SIZE + val flattenedParametersSize = + if (parametersNeedMFVCFlattening.value) parameters.sumOf { getParameterTypeSize(it) } else parameters.size + val maskSize = (flattenedParametersSize + Integer.SIZE - 1) / Integer.SIZE // Array containing the actual function arguments, masks, and +1 for DefaultConstructorMarker or MethodHandle. val arguments = arrayOfNulls(parameterSize + maskSize + 1) @@ -161,14 +164,23 @@ internal abstract class KCallableImpl : KCallable, KTypeParameterOwner var valueParameterIndex = 0 var anyOptional = false + val hasMfvcParameters = parametersNeedMFVCFlattening.value for (parameter in parameters) { + val parameterTypeSize = if (hasMfvcParameters) getParameterTypeSize(parameter) else 1 when { args.containsKey(parameter) -> { arguments[parameter.index] = args[parameter] } parameter.isOptional -> { - val maskIndex = parameterSize + (valueParameterIndex / Integer.SIZE) - arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueParameterIndex % Integer.SIZE)) + if (hasMfvcParameters) { + for (valueSubParameterIndex in valueParameterIndex until (valueParameterIndex + parameterTypeSize)) { + val maskIndex = parameterSize + (valueSubParameterIndex / Integer.SIZE) + arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueSubParameterIndex % Integer.SIZE)) + } + } else { + val maskIndex = parameterSize + (valueParameterIndex / Integer.SIZE) + arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueParameterIndex % Integer.SIZE)) + } anyOptional = true } parameter.isVararg -> {} @@ -178,7 +190,7 @@ internal abstract class KCallableImpl : KCallable, KTypeParameterOwner } if (parameter.kind == KParameter.Kind.VALUE) { - valueParameterIndex++ + valueParameterIndex += parameterTypeSize } } @@ -197,6 +209,20 @@ internal abstract class KCallableImpl : KCallable, KTypeParameterOwner } } + private val parametersNeedMFVCFlattening = lazy(LazyThreadSafetyMode.PUBLICATION) { + parameters.any { it.type.needsMultiFieldValueClassFlattening } + } + + private fun getParameterTypeSize(parameter: KParameter): Int { + require(parametersNeedMFVCFlattening.value) { "Check if parametersNeedMFVCFlattening is true before" } + return if (parameter.type.needsMultiFieldValueClassFlattening) { + val type = (parameter.type as KTypeImpl).type.asSimpleType() + getMfvcUnboxMethods(type)!!.size + } else { + 1 + } + } + private fun callAnnotationConstructor(args: Map): R { val arguments = parameters.map { parameter -> when { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt index d4e77398145..7064a143bcb 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt @@ -24,10 +24,12 @@ import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.resolve.isMultiFieldValueClass import org.jetbrains.kotlin.resolve.scopes.MemberScope import java.lang.reflect.Constructor import java.lang.reflect.Method import kotlin.jvm.internal.ClassBasedDeclarationContainer +import kotlin.reflect.jvm.internal.calls.toJvmDescriptor internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer { abstract inner class Data { @@ -119,9 +121,25 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain } fun findFunctionDescriptor(name: String, signature: String): FunctionDescriptor { - val members = if (name == "") constructorDescriptors.toList() else getFunctions(Name.identifier(name)) - val functions = members.filter { descriptor -> - RuntimeTypeMapper.mapSignature(descriptor).asString() == signature + val members: Collection + val functions: List + if (name == "") { + members = constructorDescriptors.toList() + functions = members.filter { descriptor -> + val descriptorSignature = if (descriptor.isPrimary && descriptor.containingDeclaration.isMultiFieldValueClass()) { + val initial = RuntimeTypeMapper.mapSignature(descriptor).asString() + require(initial.startsWith("constructor-impl") && initial.endsWith(")V")) { + "Invalid signature of $descriptor: $initial" + } + initial.removeSuffix("V") + descriptor.containingDeclaration.toJvmDescriptor() + } else { + RuntimeTypeMapper.mapSignature(descriptor).asString() + } + descriptorSignature == signature + } + } else { + members = getFunctions(Name.identifier(name)) + functions = members.filter { descriptor -> RuntimeTypeMapper.mapSignature(descriptor).asString() == signature } } if (functions.size != 1) { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt index dce795cf9f8..0d2ee46f69c 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KFunctionImpl.kt @@ -17,8 +17,10 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToInlineClassTypeValueParameters +import org.jetbrains.kotlin.resolve.isMultiFieldValueClass +import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToValueClassTypeValueParameters import java.lang.reflect.Constructor import java.lang.reflect.Member import java.lang.reflect.Method @@ -66,7 +68,14 @@ internal class KFunctionImpl private constructor( return@caller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, POSITIONAL_CALL, KOTLIN) container.findConstructorBySignature(jvmSignature.constructorDesc) } - is KotlinFunction -> container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc) + is KotlinFunction -> { + if (descriptor.let { it.containingDeclaration.isMultiFieldValueClass() && it is ConstructorDescriptor && it.isPrimary }) { + return@caller ValueClassAwareCaller.MultiFieldValueClassPrimaryConstructorCaller( + descriptor, container, jvmSignature.methodDesc, descriptor.valueParameters + ) + } + container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc) + } is JavaMethod -> jvmSignature.method is JavaConstructor -> jvmSignature.constructor is FakeJavaAnnotationConstructor -> { @@ -87,13 +96,16 @@ internal class KFunctionImpl private constructor( createStaticMethodCaller(member) } else -> throw KotlinReflectionInternalError("Could not compute caller for function: $descriptor (member = $member)") - }.createInlineClassAwareCallerIfNeeded(descriptor) + }.createValueClassAwareCallerIfNeeded(descriptor) } override val defaultCaller: Caller<*>? by lazy(PUBLICATION) defaultCaller@{ val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor) val member: Member? = when (jvmSignature) { is KotlinFunction -> { + if (descriptor.let { it.containingDeclaration.isMultiFieldValueClass() && it is ConstructorDescriptor && it.isPrimary }) { + throw KotlinReflectionInternalError("${descriptor.containingDeclaration} cannot have default arguments") + } container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc, !Modifier.isStatic(caller.member!!.modifiers)) } is KotlinConstructor -> { @@ -126,7 +138,7 @@ internal class KFunctionImpl private constructor( createStaticMethodCaller(member) } else -> null - }?.createInlineClassAwareCallerIfNeeded(descriptor, isDefault = true) + }?.createValueClassAwareCallerIfNeeded(descriptor, isDefault = true) } private val boundReceiver @@ -144,7 +156,7 @@ internal class KFunctionImpl private constructor( private fun createConstructorCaller( member: Constructor<*>, descriptor: FunctionDescriptor, isDefault: Boolean ): CallerImpl> { - return if (!isDefault && shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor)) { + return if (!isDefault && shouldHideConstructorDueToValueClassTypeValueParameters(descriptor)) { if (isBound) CallerImpl.AccessorForHiddenBoundConstructor(member, boundReceiver) else diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt index 79061727fef..b6319c36624 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KParameterImpl.kt @@ -18,8 +18,10 @@ package kotlin.reflect.jvm.internal import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue +import java.lang.reflect.Type import kotlin.reflect.KParameter import kotlin.reflect.KType +import kotlin.reflect.jvm.internal.calls.ValueClassAwareCaller internal class KParameterImpl( val callable: KCallableImpl<*>, @@ -39,6 +41,27 @@ internal class KParameterImpl( return if (name.isSpecial) null else name.asString() } + + private fun compoundType(vararg types: Type): Type = when (types.size) { + 0 -> throw KotlinReflectionNotSupportedError("Expected at least 1 type for compound type") + 1 -> types.single() + else -> CompoundTypeImpl(types) + } + + private class CompoundTypeImpl(val types: Array) : Type { + private val hashCode = types.contentHashCode() + override fun getTypeName(): String { + return types.joinToString(", ", "[", "]") + } + + override fun equals(other: Any?): Boolean = + other is CompoundTypeImpl && this.types contentEquals other.types + + override fun hashCode(): Int = hashCode + + override fun toString(): String = typeName + } + override val type: KType get() = KTypeImpl(descriptor.type) { val descriptor = descriptor @@ -53,7 +76,16 @@ internal class KParameterImpl( (callable.descriptor.containingDeclaration as ClassDescriptor).toJavaClass() ?: throw KotlinReflectionInternalError("Cannot determine receiver Java type of inherited declaration: $descriptor") } else { - callable.caller.parameterTypes[index] + when (val caller = callable.caller) { + is ValueClassAwareCaller -> { + val slice = caller.getRealSlicesOfParameters(index) + val parameterTypes = caller.parameterTypes.slice(slice) + compoundType(*parameterTypes.toTypedArray()) + } + is ValueClassAwareCaller.MultiFieldValueClassPrimaryConstructorCaller -> + compoundType(*caller.originalParametersGroups[index].toTypedArray()) + else -> caller.parameterTypes[index] + } } } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt index eeaa98d22b5..fa9596aba7e 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPropertyImpl.kt @@ -271,8 +271,9 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool if (property.descriptor.isUnderlyingPropertyOfInlineClass() && property.descriptor.visibility == DescriptorVisibilities.INTERNAL ) { - val unboxMethod = property.descriptor.containingDeclaration.toInlineClass()?.getUnboxMethod(property.descriptor) - ?: throw KotlinReflectionInternalError("Underlying property of inline class $property should have a field") + val unboxMethod = + property.descriptor.containingDeclaration.toInlineClass()?.getInlineClassUnboxMethod(property.descriptor) + ?: throw KotlinReflectionInternalError("Underlying property of inline class $property should have a field") if (isBound) InternalUnderlyingValOfInlineClass.Bound(unboxMethod, boundReceiver) else InternalUnderlyingValOfInlineClass.Unbound(unboxMethod) } else { @@ -316,7 +317,7 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool return if (isBound) CallerImpl.Method.BoundInstance(accessor, boundReceiver) else CallerImpl.Method.Instance(accessor) } - }.createInlineClassAwareCallerIfNeeded(descriptor) + }.createValueClassAwareCallerIfNeeded(descriptor) } private fun PropertyDescriptor.isJvmFieldPropertyInCompanionObject(): Boolean { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt index 5d648152648..8d117dfe1cd 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/RuntimeTypeMapper.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.DescriptorFactory import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor import org.jetbrains.kotlin.resolve.isInlineClass +import org.jetbrains.kotlin.resolve.isMultiFieldValueClass import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor @@ -51,6 +52,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import java.lang.reflect.Constructor import java.lang.reflect.Field import java.lang.reflect.Method +import kotlin.reflect.jvm.internal.calls.toJvmDescriptor internal sealed class JvmFunctionSignature { abstract fun asString(): String @@ -174,10 +176,27 @@ internal object RuntimeTypeMapper { } if (proto is ProtoBuf.Constructor) { JvmProtoBufUtil.getJvmConstructorSignature(proto, function.nameResolver, function.typeTable)?.let { signature -> - return if (possiblySubstitutedFunction.containingDeclaration.isInlineClass()) - JvmFunctionSignature.KotlinFunction(signature) - else - JvmFunctionSignature.KotlinConstructor(signature) + return when { + possiblySubstitutedFunction.containingDeclaration.isInlineClass() -> + JvmFunctionSignature.KotlinFunction(signature) + possiblySubstitutedFunction.containingDeclaration.isMultiFieldValueClass() -> { + val realSignature = if ((possiblySubstitutedFunction as ConstructorDescriptor).isPrimary) { + require(signature.name == "constructor-impl" && signature.desc.endsWith(")V")) { "Invalid signature: $signature" } + signature + } else { + require(signature.name == "constructor-impl") { "Invalid signature: $signature" } + val constructedClass = possiblySubstitutedFunction.constructedClass.toJvmDescriptor() + if (signature.desc.endsWith(")V")) { + signature.copy(desc = signature.desc.removeSuffix("V") + constructedClass) + } else { + require(signature.desc.endsWith(constructedClass)) { "Invalid signature: $signature" } + signature + } + } + JvmFunctionSignature.KotlinFunction(realSignature) + } + else -> JvmFunctionSignature.KotlinConstructor(signature) + } } } return mapJvmFunctionSignature(function) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt index 66656367b19..7b525327f28 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/CallerImpl.kt @@ -122,7 +122,7 @@ internal sealed class CallerImpl( } } - class BoundStatic(method: ReflectMethod, private val boundReceiver: Any?) : BoundCaller, Method( + class BoundStatic(method: ReflectMethod, internal val boundReceiver: Any?) : BoundCaller, Method( method, requiresInstance = false, parameterTypes = method.genericParameterTypes.dropFirst() ) { override fun call(args: Array<*>): Any? { @@ -131,6 +131,21 @@ internal sealed class CallerImpl( } } + class BoundStaticMultiFieldValueClass( + method: ReflectMethod, internal val boundReceiverComponents: Array + ) : BoundCaller, Method( + method = method, + requiresInstance = false, + parameterTypes = method.genericParameterTypes.drop(boundReceiverComponents.size).toTypedArray() + ) { + override fun call(args: Array<*>): Any? { + checkArguments(args) + return callMethod(null, arrayOf(*boundReceiverComponents, *args)) + } + + val receiverComponentsCount: Int get() = boundReceiverComponents.size + } + class BoundInstance(method: ReflectMethod, private val boundReceiver: Any?) : BoundCaller, Method(method, requiresInstance = false) { override fun call(args: Array<*>): Any? { diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/ValueClassAwareCaller.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/ValueClassAwareCaller.kt index dd3540a7d8f..feacd27f8c8 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/ValueClassAwareCaller.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/calls/ValueClassAwareCaller.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2023 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. */ @@ -7,29 +7,50 @@ package kotlin.reflect.jvm.internal.calls import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.runtime.structure.desc import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.descriptorUtil.classId +import org.jetbrains.kotlin.resolve.descriptorUtil.multiFieldValueClassRepresentation import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.asSimpleType import java.lang.reflect.Member import java.lang.reflect.Method import java.lang.reflect.Type +import kotlin.reflect.jvm.internal.KDeclarationContainerImpl import kotlin.reflect.jvm.internal.KotlinReflectionInternalError import kotlin.reflect.jvm.internal.defaultPrimitiveValue import kotlin.reflect.jvm.internal.toJavaClass /** - * A caller that is used whenever the declaration has inline classes in its parameter types or return type. - * Each argument of an inline class type is unboxed, and the return value (if it's of an inline class type) is boxed. + * A caller that is used whenever the declaration has value classes in its parameter types or inline class in return type. + * Each argument of an value class type is unboxed, and the return value (if it's of an inline class type) is boxed. */ -internal class InlineClassAwareCaller( +internal class ValueClassAwareCaller( descriptor: CallableMemberDescriptor, - private val caller: Caller, + oldCaller: Caller, private val isDefault: Boolean ) : Caller { - override val member: M - get() = caller.member + + private val caller: Caller = if (oldCaller is CallerImpl.Method.BoundStatic) { + val receiverType = (descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter)?.type + if (receiverType != null && receiverType.needsMfvcFlattening()) { + val unboxMethods = getMfvcUnboxMethods(receiverType.asSimpleType())!! + val boundReceiverComponents = unboxMethods.map { it.invoke(oldCaller.boundReceiver) }.toTypedArray() + @Suppress("UNCHECKED_CAST") + CallerImpl.Method.BoundStaticMultiFieldValueClass(oldCaller.member, boundReceiverComponents) as Caller + } else { + oldCaller + } + } else { + oldCaller + } + + override val member: M = caller.member + override val returnType: Type get() = caller.returnType @@ -37,24 +58,18 @@ internal class InlineClassAwareCaller( override val parameterTypes: List get() = caller.parameterTypes - private class BoxUnboxData(val argumentRange: IntRange, val unbox: Array, val box: Method?) { - operator fun component1(): IntRange = argumentRange - operator fun component2(): Array = unbox - operator fun component3(): Method? = box - } + private class BoxUnboxData(val argumentRange: IntRange, val unboxParameters: Array?>, val box: Method?) private val data: BoxUnboxData = run { val box = descriptor.returnType!!.toInlineClass()?.getBoxMethod(descriptor) - - if (descriptor.isGetterOfUnderlyingPropertyOfInlineClass()) { - // Getter of the underlying val of an inline class is always called on a boxed receiver, - // no argument boxing/unboxing is required. - // However, its result might require boxing if it is an inline class type. + if (descriptor.isGetterOfUnderlyingPropertyOfValueClass()) { + // Getter of the underlying val of a value class is always called on a boxed receiver, + // no argument unboxing is required. return@run BoxUnboxData(IntRange.EMPTY, emptyArray(), box) } val shift = when { - caller is CallerImpl.Method.BoundStatic -> { + caller is CallerImpl.Method.BoundStatic || caller is CallerImpl.Method.BoundStaticMultiFieldValueClass -> { // Bound reference to a static method is only possible for a top level extension function/property, // and in that case the number of expected arguments is one less than usual, hence -1 -1 @@ -64,9 +79,9 @@ internal class InlineClassAwareCaller( if (caller is BoundCaller) -1 else 0 descriptor.dispatchReceiverParameter != null && caller !is BoundCaller -> { - // If we have an unbound reference to the inline class member, + // If we have an unbound reference to the value class member, // its receiver (which is passed as argument 0) should also be unboxed. - if (descriptor.containingDeclaration.isInlineClass()) + if (descriptor.containingDeclaration.isValueClass()) 0 else 1 @@ -75,117 +90,229 @@ internal class InlineClassAwareCaller( else -> 0 } - val kotlinParameterTypes: List = ArrayList().also { kotlinParameterTypes -> - val extensionReceiverType = descriptor.extensionReceiverParameter?.type - if (extensionReceiverType != null) { - kotlinParameterTypes.add(extensionReceiverType) - } else if (descriptor is ConstructorDescriptor) { - val constructedClass = descriptor.constructedClass - if (constructedClass.isInner) { - kotlinParameterTypes.add((constructedClass.containingDeclaration as ClassDescriptor).defaultType) - } - } else { - val containingDeclaration = descriptor.containingDeclaration - if (containingDeclaration is ClassDescriptor && containingDeclaration.isInlineClass()) { - kotlinParameterTypes.add(containingDeclaration.defaultType) - } - } + val flattenedShift = if (caller is CallerImpl.Method.BoundStaticMultiFieldValueClass) -caller.receiverComponentsCount else shift - descriptor.valueParameters.mapTo(kotlinParameterTypes, ValueParameterDescriptor::getType) - } + val kotlinParameterTypes: List = makeKotlinParameterTypes(descriptor) { isValueClass() } + + fun typeSize(type: KotlinType): Int = getMfvcUnboxMethods(type.asSimpleType())?.size ?: 1 // If the default argument is set, - // (kotlinParameterTypes.size + Integer.SIZE - 1) / Integer.SIZE masks and one marker are added to the end of the argument. - val extraArgumentsTail = (if (isDefault) ((kotlinParameterTypes.size + Integer.SIZE - 1) / Integer.SIZE) + 1 else 0) + - (if (descriptor is FunctionDescriptor && descriptor.isSuspend) 1 else 0) - val expectedArgsSize = kotlinParameterTypes.size + shift + extraArgumentsTail - if (arity != expectedArgsSize) { - throw KotlinReflectionInternalError( - "Inconsistent number of parameters in the descriptor and Java reflection object: $arity != $expectedArgsSize\n" + - "Calling: $descriptor\n" + - "Parameter types: ${this.parameterTypes})\n" + - "Default: $isDefault" - ) - } + // (kotlinParameterTypes.size + Int.SIZE_BITS - 1) / Int.SIZE_BITS masks and one marker are added to the end of the argument. + val extraArgumentsTail = + (if (isDefault) ((kotlinParameterTypes.sumOf(::typeSize) + Int.SIZE_BITS - 1) / Int.SIZE_BITS) + 1 else 0) + + (if (descriptor is FunctionDescriptor && descriptor.isSuspend) 1 else 0) + val expectedArgsSize = kotlinParameterTypes.sumOf(::typeSize) + flattenedShift + extraArgumentsTail + checkParametersSize(expectedArgsSize, descriptor, isDefault) // maxOf is needed because in case of a bound top level extension, shift can be -1 (see above). But in that case, we need not unbox // the extension receiver argument, since it has already been unboxed at compile time and generated into the reference val argumentRange = maxOf(shift, 0) until (kotlinParameterTypes.size + shift) val unbox = Array(expectedArgsSize) { i -> - if (i in argumentRange) { - kotlinParameterTypes[i - shift].toInlineClass()?.getUnboxMethod(descriptor) - } else null + if (i in argumentRange) + getValueClassUnboxMethods(kotlinParameterTypes[i - shift].asSimpleType(), descriptor) + else null } BoxUnboxData(argumentRange, unbox, box) } + private val slices = buildList { + var currentOffset = when (caller) { + is CallerImpl.Method.BoundStaticMultiFieldValueClass -> caller.boundReceiverComponents.size + is CallerImpl.Method.BoundStatic -> 1 + else -> 0 + } + if (currentOffset > 0) { + add(0 until currentOffset) + } + for (parameterUnboxMethods in data.unboxParameters) { + val length = parameterUnboxMethods?.size ?: 1 + add(currentOffset until (currentOffset + length)) + currentOffset += length + } + }.toTypedArray() + + fun getRealSlicesOfParameters(index: Int): IntRange = when { + index in slices.indices -> slices[index] + slices.isEmpty() -> index..index + else -> { + val start = (index - slices.size) + (slices.last().last + 1) + start..start + } + } + + private val hasMfvcParameters = data.argumentRange.any { (data.unboxParameters[it] ?: return@any false).size > 1 } + override fun call(args: Array<*>): Any? { - val (range, unbox, box) = data + val range = data.argumentRange + val unbox = data.unboxParameters + val box = data.box - @Suppress("UNCHECKED_CAST") - val unboxed = Array(args.size) { index -> - val arg = args[index] - - if (index in range) { - // Note that arg may be null in case we're calling a $default method and it's an optional parameter of an inline class type - val method = unbox[index] - - if (method != null) { - if (arg != null) { - method.invoke(arg) + val unboxedArguments = when { + range.isEmpty() -> args + hasMfvcParameters -> buildList(args.size) { + for (index in 0 until range.first) { + add(args[index]) + } + for (index in range) { + val methods = unbox[index] + val arg = args[index] + // Note that arg may be null in case we're calling a $default method, and it's an optional parameter of a value class type + if (methods != null) { + methods.mapTo(this) { if (arg != null) it.invoke(arg) else defaultPrimitiveValue(it.returnType) } } else { - defaultPrimitiveValue(method.returnType) + add(arg) + } + } + for (index in (range.last + 1)..args.lastIndex) { + add(args[index]) + } + }.toTypedArray() + else -> Array(args.size) { index -> + if (index in range) { + val method = unbox[index]?.single() + val arg = args[index] + // Note that arg may be null in case we're calling a $default method, and it's an optional parameter of a inline class type + when { + method == null -> arg + arg != null -> method.invoke(arg) + else -> defaultPrimitiveValue(method.returnType) } } else { - arg + args[index] } - } else { - arg } } - val result = caller.call(unboxed) + val result = caller.call(unboxedArguments) + // box is not null only for inline classes return box?.invoke(null, result) ?: result } + + class MultiFieldValueClassPrimaryConstructorCaller( + descriptor: FunctionDescriptor, + container: KDeclarationContainerImpl, + constructorDesc: String, + originalParameters: List + ) : Caller { + private val constructorImpl = container.findMethodBySignature("constructor-impl", constructorDesc)!! + private val boxMethod = container.findMethodBySignature("box-impl", constructorDesc.removeSuffix("V") + container.jClass.desc)!! + private val parameterUnboxMethods: List?> = originalParameters.map { parameter -> + getValueClassUnboxMethods(parameter.type.asSimpleType(), descriptor) + } + override val member: Nothing? + get() = null + override val returnType: Type + get() = boxMethod.returnType + + val originalParametersGroups: List>> = originalParameters.mapIndexed { index, it -> + val classDescriptor = it.type.constructor.declarationDescriptor as ClassDescriptor + parameterUnboxMethods[index]?.map { it.returnType } ?: listOf(classDescriptor.toJavaClass()!!) + } + + override val parameterTypes: List = originalParametersGroups.flatten() + + override fun call(args: Array<*>): Any? { + val newArgs = (args zip parameterUnboxMethods) + .flatMap { (arg, unboxMethods) -> unboxMethods?.map { it.invoke(arg) } ?: listOf(arg) }.toTypedArray() + constructorImpl.invoke(null, *newArgs) + return boxMethod.invoke(null, *newArgs) + } + } } -internal fun Caller.createInlineClassAwareCallerIfNeeded( +internal fun ClassifierDescriptor.toJvmDescriptor(): String = ClassMapperLite.mapClass(classId!!.asString()) + + +private fun getValueClassUnboxMethods(type: SimpleType, descriptor: CallableMemberDescriptor): List? = + getMfvcUnboxMethods(type) ?: type.toInlineClass()?.getInlineClassUnboxMethod(descriptor)?.let(::listOf) + +internal fun getMfvcUnboxMethods(type: SimpleType): List? { + fun getUnboxMethodNameSuffixes(type: SimpleType): List? = + if (type.needsMfvcFlattening()) (type.constructor.declarationDescriptor as ClassDescriptor) + .multiFieldValueClassRepresentation!!.underlyingPropertyNamesToTypes.flatMap { (name, innerType) -> + getUnboxMethodNameSuffixes(innerType)?.map { "${name.identifier}-${it}" } ?: listOf(name.identifier) + } + else null + + val unboxMethodsNames = getUnboxMethodNameSuffixes(type.asSimpleType())?.map { "unbox-impl-$it" } ?: return null + val javaClass = (type.constructor.declarationDescriptor as ClassDescriptor).toJavaClass()!! + return unboxMethodsNames.map { javaClass.getDeclaredMethod(it) } +} + +private fun Caller<*>.checkParametersSize( + expectedArgsSize: Int, + descriptor: CallableMemberDescriptor, + isDefault: Boolean, +) { + if (arity != expectedArgsSize) { + throw KotlinReflectionInternalError( + "Inconsistent number of parameters in the descriptor and Java reflection object: $arity != $expectedArgsSize\n" + + "Calling: $descriptor\n" + + "Parameter types: ${this.parameterTypes})\n" + + "Default: $isDefault" + ) + } +} + +private fun makeKotlinParameterTypes( + descriptor: CallableMemberDescriptor, isSpecificClass: ClassDescriptor.() -> Boolean +): List = ArrayList().also { kotlinParameterTypes -> + val extensionReceiverType = descriptor.extensionReceiverParameter?.type + if (extensionReceiverType != null) { + kotlinParameterTypes.add(extensionReceiverType) + } else if (descriptor is ConstructorDescriptor) { + val constructedClass = descriptor.constructedClass + if (constructedClass.isInner) { + kotlinParameterTypes.add((constructedClass.containingDeclaration as ClassDescriptor).defaultType) + } + } else { + val containingDeclaration = descriptor.containingDeclaration + if (containingDeclaration is ClassDescriptor && containingDeclaration.isSpecificClass()) { + kotlinParameterTypes.add(containingDeclaration.defaultType) + } + } + + descriptor.valueParameters.mapTo(kotlinParameterTypes, ValueParameterDescriptor::getType) +} + +internal fun Caller.createValueClassAwareCallerIfNeeded( descriptor: CallableMemberDescriptor, isDefault: Boolean = false ): Caller { - val needsInlineAwareCaller: Boolean = - descriptor.isGetterOfUnderlyingPropertyOfInlineClass() || - descriptor.valueParameters.any { it.type.isInlineClassType() } || + val needsValueClassAwareCaller: Boolean = + descriptor.isGetterOfUnderlyingPropertyOfValueClass() || + descriptor.contextReceiverParameters.any { it.type.isValueClassType() } || + descriptor.valueParameters.any { it.type.isValueClassType() } || descriptor.returnType?.isInlineClassType() == true || - this !is BoundCaller && descriptor.hasInlineClassReceiver() + descriptor.hasValueClassReceiver() - return if (needsInlineAwareCaller) InlineClassAwareCaller(descriptor, this, isDefault) else this + return if (needsValueClassAwareCaller) ValueClassAwareCaller(descriptor, this, isDefault) else this } -private fun CallableMemberDescriptor.hasInlineClassReceiver() = - expectedReceiverType?.isInlineClassType() == true +private fun CallableMemberDescriptor.hasValueClassReceiver() = + expectedReceiverType?.isValueClassType() == true -internal fun Class<*>.getUnboxMethod(descriptor: CallableMemberDescriptor): Method = +internal fun Class<*>.getInlineClassUnboxMethod(descriptor: CallableMemberDescriptor): Method = try { getDeclaredMethod("unbox" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS) } catch (e: NoSuchMethodException) { throw KotlinReflectionInternalError("No unbox method found in inline class: $this (calling $descriptor)") } -internal fun Class<*>.getBoxMethod(descriptor: CallableMemberDescriptor): Method = +private fun Class<*>.getBoxMethod(descriptor: CallableMemberDescriptor): Method = try { - getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getUnboxMethod(descriptor).returnType) + getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getInlineClassUnboxMethod(descriptor).returnType) } catch (e: NoSuchMethodException) { throw KotlinReflectionInternalError("No box method found in inline class: $this (calling $descriptor)") } -internal fun KotlinType.toInlineClass(): Class<*>? { +private fun KotlinType.toInlineClass(): Class<*>? { // See computeExpandedTypeForInlineClass. - // TODO: add tests on type parameters with inline class bounds. - // TODO: add tests on usages of inline classes in Java. + // TODO: add tests on type parameters with value class bounds. + // TODO: add tests on usages of value classes in Java. val klass = constructor.declarationDescriptor.toInlineClass() ?: return null if (!TypeUtils.isNullableType(this)) return klass @@ -217,7 +344,7 @@ internal fun Any?.coerceToExpectedReceiverType(descriptor: CallableMemberDescrip if (descriptor is PropertyDescriptor && descriptor.isUnderlyingPropertyOfInlineClass()) return this val expectedReceiverType = descriptor.expectedReceiverType - val unboxMethod = expectedReceiverType?.toInlineClass()?.getUnboxMethod(descriptor) ?: return this + val unboxMethod = expectedReceiverType?.toInlineClass()?.getInlineClassUnboxMethod(descriptor) ?: return this return unboxMethod.invoke(this) } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index e09ed34ad60..b848204ec6b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.constants.* import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.isInlineClassType +import org.jetbrains.kotlin.resolve.needsMfvcFlattening import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer import java.lang.reflect.Field @@ -268,6 +269,8 @@ internal fun deserializeToDescriptor( internal val KType.isInlineClassType: Boolean get() = (this as? KTypeImpl)?.type?.isInlineClassType() == true +internal val KType.needsMultiFieldValueClassFlattening: Boolean + get() = (this as? KTypeImpl)?.type?.needsMfvcFlattening() == true internal fun defaultPrimitiveValue(type: Type): Any? = if (type is Class<*> && type.isPrimitive) { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java index 037751e2598..c0d77ef1e68 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/JsCodegenBoxTestGenerated.java @@ -31931,6 +31931,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } } @Nested @@ -32391,6 +32401,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java index 22e5a536dc8..3f973eeb91f 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsCodegenBoxTestGenerated.java @@ -32117,6 +32117,16 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest { } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @Nested @@ -32577,6 +32587,16 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java index 53e4cda6f16..79aa547c7e1 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsCodegenBoxTestGenerated.java @@ -32117,6 +32117,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @Nested @@ -32577,6 +32587,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @Nested diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java index 2ce5a5182a0..e519b2ec6f0 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsES6CodegenBoxTestGenerated.java @@ -32117,6 +32117,16 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); + } + } } @Nested @@ -32577,6 +32587,16 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); + } + } } @Nested diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java index ec8207c0f35..0f458e09553 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/K2NativeCodegenBoxTestGenerated.java @@ -35571,6 +35571,19 @@ public class K2NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTes } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegenK2") + @UseExtTestCaseGroupProvider() + @K2Pipeline() + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + } } @Nested @@ -36091,6 +36104,19 @@ public class K2NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegenK2") + @UseExtTestCaseGroupProvider() + @K2Pipeline() + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + } } @Nested diff --git a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java index 9891efe39fd..b43c618184a 100644 --- a/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java +++ b/native/native.tests/tests-gen/org/jetbrains/kotlin/konan/blackboxtest/NativeCodegenBoxTestGenerated.java @@ -35148,6 +35148,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegen") + @UseExtTestCaseGroupProvider() + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + } } @Nested @@ -35652,6 +35664,18 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @Tag("codegen") + @UseExtTestCaseGroupProvider() + public class ValueClasses { + @Test + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.NATIVE, true); + } + } } @Nested diff --git a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/IrCodegenBoxWasmTestGenerated.java b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/IrCodegenBoxWasmTestGenerated.java index 40b7cae90ed..c80d55ab159 100644 --- a/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/IrCodegenBoxWasmTestGenerated.java +++ b/wasm/wasm.tests/tests-gen/org/jetbrains/kotlin/wasm/test/IrCodegenBoxWasmTestGenerated.java @@ -28720,6 +28720,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest } } } + + @TestMetadata("compiler/testData/codegen/box/reflection/call/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/call/valueClasses"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/reflection/callBy") @@ -29178,6 +29191,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/types"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); } } + + @TestMetadata("compiler/testData/codegen/box/reflection/mapping/valueClasses") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ValueClasses extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInValueClasses() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/mapping/valueClasses"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/reflection/methodsFromAny")