diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt index 7b61696136f..992d9f0f095 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/DefaultParameterValueSubstitutor.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.codegen.JvmCodegenUtil.getDispatchReceiverParameterForConstructorCall import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtParameter @@ -132,10 +133,13 @@ class DefaultParameterValueSubstitutor(val state: GenerationState) { val remainingParametersDeclarations = remainingParameters.map { DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtParameter } + val generateAsFinal = + functionDescriptor.modality == Modality.FINAL || + state.languageVersionSettings.supportsFeature(LanguageFeature.GenerateJvmOverloadsAsFinal) val flags = baseMethodFlags or (if (isStatic) Opcodes.ACC_STATIC else 0) or - (if (functionDescriptor.modality == Modality.FINAL && functionDescriptor !is ConstructorDescriptor) Opcodes.ACC_FINAL else 0) or + (if (generateAsFinal && functionDescriptor !is ConstructorDescriptor) Opcodes.ACC_FINAL else 0) or (if (remainingParameters.lastOrNull()?.varargElementType != null) Opcodes.ACC_VARARGS else 0) val signature = typeMapper.mapSignatureWithCustomParameters(functionDescriptor, contextKind, remainingParameters, false) val mv = classBuilder.newMethod( diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt index a1dfbb50dea..c56d6917464 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmOverloadsAnnotationLowering.kt @@ -6,11 +6,13 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.ClassLoweringPass -import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl @@ -137,13 +139,16 @@ private class JvmOverloadsAnnotationLowering(val context: JvmBackendContext) : C } is IrSimpleFunction -> { val descriptor = WrappedSimpleFunctionDescriptor(oldFunction.descriptor.annotations) + val modality = + if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.GenerateJvmOverloadsAsFinal)) Modality.FINAL + else oldFunction.modality IrFunctionImpl( UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.JVM_OVERLOADS_WRAPPER, IrSimpleFunctionSymbolImpl(descriptor), oldFunction.name, oldFunction.visibility, - oldFunction.modality, + modality, returnType = oldFunction.returnType, isInline = oldFunction.isInline, isExternal = false, @@ -151,7 +156,7 @@ private class JvmOverloadsAnnotationLowering(val context: JvmBackendContext) : C isSuspend = oldFunction.isSuspend, isExpect = false, isFakeOverride = false, - isOperator = false + isOperator = false, ).apply { descriptor.bind(this) } diff --git a/compiler/testData/writeFlags/jvmOverloads/openFunction.kt b/compiler/testData/writeFlags/jvmOverloads/openFunction.kt new file mode 100644 index 00000000000..30ee28b35fa --- /dev/null +++ b/compiler/testData/writeFlags/jvmOverloads/openFunction.kt @@ -0,0 +1,18 @@ +// WITH_RUNTIME + +open class Foo { + @JvmOverloads + open fun bar(x: Int = 42, y: Int = -1): Int = x + y +} + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, (II)I +// FLAGS: ACC_PUBLIC + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, (I)I +// FLAGS: ACC_PUBLIC, ACC_FINAL + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, ()I +// FLAGS: ACC_PUBLIC, ACC_FINAL diff --git a/compiler/testData/writeFlags/jvmOverloads/openFunction_1_3.kt b/compiler/testData/writeFlags/jvmOverloads/openFunction_1_3.kt new file mode 100644 index 00000000000..0cd5cc2502f --- /dev/null +++ b/compiler/testData/writeFlags/jvmOverloads/openFunction_1_3.kt @@ -0,0 +1,19 @@ +// !LANGUAGE: -GenerateJvmOverloadsAsFinal +// WITH_RUNTIME + +open class Foo { + @JvmOverloads + open fun bar(x: Int = 42, y: Int = -1): Int = x + y +} + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, (II)I +// FLAGS: ACC_PUBLIC + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, (I)I +// FLAGS: ACC_PUBLIC + +// TESTED_OBJECT_KIND: function +// TESTED_OBJECTS: Foo, bar, ()I +// FLAGS: ACC_PUBLIC diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/flags/AbstractWriteFlagsTest.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/flags/AbstractWriteFlagsTest.java index 0019aab041a..81afa200e0c 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/flags/AbstractWriteFlagsTest.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/flags/AbstractWriteFlagsTest.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.codegen.flags; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.codegen.CodegenTestCase; import org.jetbrains.org.objectweb.asm.*; @@ -34,7 +35,7 @@ import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPref * Test correctness of written flags in class file * * TESTED_OBJECT_KIND - maybe class, function or property - * TESTED_OBJECTS - className, [function/property name] + * TESTED_OBJECTS - className, [function/property name], [function/property signature] * FLAGS - only flags which must be true (could be skipped if ABSENT is TRUE) * ABSENT - true or false, optional (false by default) * @@ -46,6 +47,10 @@ import static org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPref * TESTED_OBJECT_KIND: property * TESTED_OBJECTS: Test, prop$delegate * FLAGS: ACC_STATIC, ACC_FINAL, ACC_PRIVATE + * + * TESTED_OBJECT_KIND: function + * TESTED_OBJECTS: Test, function, (ILjava/lang/String;)[Ljava/lang/Object; + * FLAGS: ACC_PUBLIC, ACC_SYNTHETIC */ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { @@ -72,11 +77,11 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { assertNotNull(outputFile); ClassReader cr = new ClassReader(outputFile.asByteArray()); - TestClassVisitor classVisitor = getClassVisitor(testedObject.kind, testedObject.name, false); + TestClassVisitor classVisitor = getClassVisitor(testedObject, false); cr.accept(classVisitor, ClassReader.SKIP_CODE); if (!classVisitor.isExists()) { - classVisitor = getClassVisitor(testedObject.kind, testedObject.name, true); + classVisitor = getClassVisitor(testedObject, true); cr.accept(classVisitor, ClassReader.SKIP_CODE); } @@ -100,7 +105,7 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { TestedObject testObject = new TestedObject(); testObject.textData = testData; List testedObjects = findListWithPrefixes(testData, "// TESTED_OBJECTS: "); - assertTrue("Cannot find TESTED_OBJECTS instruction", !testedObjects.isEmpty()); + assertFalse("Cannot find TESTED_OBJECTS instruction", testedObjects.isEmpty()); testObject.containingClass = testedObjects.get(0); if (testedObjects.size() == 1) { testObject.name = testedObjects.get(0); @@ -108,9 +113,13 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { else if (testedObjects.size() == 2) { testObject.name = testedObjects.get(1); } + else if (testedObjects.size() == 3) { + testObject.name = testedObjects.get(1); + testObject.signature = testedObjects.get(2); + } else { throw new IllegalArgumentException( - "TESTED_OBJECTS instruction must contain one (for class) or two (for function and property) values"); + "TESTED_OBJECTS instruction must contain one (for class), two or three (for function and property) values"); } testObject.kind = findStringWithPrefixes(testData, "// TESTED_OBJECT_KIND: "); @@ -120,7 +129,7 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { } objects.add(testObject); } - assertTrue("Test description not present!", !objects.isEmpty()); + assertFalse("Test description not present!", objects.isEmpty()); return objects; } @@ -130,27 +139,28 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { public boolean isFullContainingClassName = true; public String kind; public String textData; + public String signature; @Override public String toString() { - return "Class = " + containingClass + ", name = " + name + ", kind = " + kind; + return "Class = " + containingClass + ", name = " + name + ", kind = " + kind + + (signature != null ? ", signature = " + signature : ""); } } - private static TestClassVisitor getClassVisitor(String visitorKind, String testedObjectName, boolean allowSynthetic) { - switch (visitorKind) { + private static TestClassVisitor getClassVisitor(@NotNull TestedObject object, boolean allowSynthetic) { + switch (object.kind) { case "class": return new ClassFlagsVisitor(); case "function": - return new FunctionFlagsVisitor(testedObjectName, allowSynthetic); + return new FunctionFlagsVisitor(object.name, object.signature, allowSynthetic); case "property": - return new PropertyFlagsVisitor(testedObjectName); + return new PropertyFlagsVisitor(object.name, object.signature); case "innerClass": - return new InnerClassFlagsVisitor(testedObjectName); - default: + return new InnerClassFlagsVisitor(object.name); + default: + throw new IllegalArgumentException("Value of TESTED_OBJECT_KIND is incorrect: " + object.kind); } - - throw new IllegalArgumentException("Value of TESTED_OBJECT_KIND is incorrect: " + visitorKind); } protected static abstract class TestClassVisitor extends ClassVisitor { @@ -170,7 +180,7 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { private static int getExpectedFlags(String text) { int expectedAccess = 0; - Class klass = Opcodes.class; + Class klass = Opcodes.class; List flags = findListWithPrefixes(text, "// FLAGS: "); for (String flag : flags) { try { @@ -202,16 +212,18 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { private static class FunctionFlagsVisitor extends TestClassVisitor { private int access = 0; private final String funName; + private final String funSignature; private final boolean allowSynthetic; - public FunctionFlagsVisitor(String name, boolean allowSynthetic) { - funName = name; + public FunctionFlagsVisitor(@NotNull String name, @Nullable String signature, boolean allowSynthetic) { + this.funName = name; + this.funSignature = signature; this.allowSynthetic = allowSynthetic; } @Override public MethodVisitor visitMethod(int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions) { - if (name.equals(funName)) { + if (name.equals(funName) && (funSignature == null || funSignature.equals(desc))) { if (!allowSynthetic && (access & Opcodes.ACC_SYNTHETIC) != 0) return null; this.access = access; isExists = true; @@ -228,14 +240,16 @@ public abstract class AbstractWriteFlagsTest extends CodegenTestCase { private static class PropertyFlagsVisitor extends TestClassVisitor { private int access = 0; private final String propertyName; + private final String propertySignature; - public PropertyFlagsVisitor(String name) { - propertyName = name; + public PropertyFlagsVisitor(@NotNull String name, @Nullable String signature) { + this.propertyName = name; + this.propertySignature = signature; } @Override public FieldVisitor visitField(int access, @NotNull String name, @NotNull String desc, String signature, Object value) { - if (name.equals(propertyName)) { + if (name.equals(propertyName) || (propertySignature == null || propertySignature.equals(desc))) { this.access = access; isExists = true; } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java index f7bc694baf3..b425aa44df7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/flags/WriteFlagsTestGenerated.java @@ -821,6 +821,29 @@ public class WriteFlagsTestGenerated extends AbstractWriteFlagsTest { } } + @TestMetadata("compiler/testData/writeFlags/jvmOverloads") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmOverloads extends AbstractWriteFlagsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInJvmOverloads() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/jvmOverloads"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("openFunction.kt") + public void testOpenFunction() throws Exception { + runTest("compiler/testData/writeFlags/jvmOverloads/openFunction.kt"); + } + + @TestMetadata("openFunction_1_3.kt") + public void testOpenFunction_1_3() throws Exception { + runTest("compiler/testData/writeFlags/jvmOverloads/openFunction_1_3.kt"); + } + } + @TestMetadata("compiler/testData/writeFlags/lambda") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java index b8c7762e643..0ac9221c8bf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrWriteFlagsTestGenerated.java @@ -821,6 +821,29 @@ public class IrWriteFlagsTestGenerated extends AbstractIrWriteFlagsTest { } } + @TestMetadata("compiler/testData/writeFlags/jvmOverloads") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JvmOverloads extends AbstractIrWriteFlagsTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInJvmOverloads() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/writeFlags/jvmOverloads"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("openFunction.kt") + public void testOpenFunction() throws Exception { + runTest("compiler/testData/writeFlags/jvmOverloads/openFunction.kt"); + } + + @TestMetadata("openFunction_1_3.kt") + public void testOpenFunction_1_3() throws Exception { + runTest("compiler/testData/writeFlags/jvmOverloads/openFunction_1_3.kt"); + } + } + @TestMetadata("compiler/testData/writeFlags/lambda") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 50bb64aa949..debbc53970b 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -124,6 +124,7 @@ enum class LanguageFeature( DoNotGenerateThrowsForDelegatedKotlinMembers(KOTLIN_1_4), ProperIeee754Comparisons(KOTLIN_1_4, kind = BUG_FIX), FunctionalInterfaceConversion(KOTLIN_1_4, kind = UNSTABLE_FEATURE), + GenerateJvmOverloadsAsFinal(KOTLIN_1_4), ProhibitSpreadOnSignaturePolymorphicCall(KOTLIN_1_5, kind = BUG_FIX), ProhibitInvisibleAbstractMethodsInSuperclasses(KOTLIN_1_5, kind = BUG_FIX),