Do not generate Throws attribute for delegated members from Kotlin interfaces

#KT-31763 Fixed
 #KT-35834
This commit is contained in:
Alexander Udalov
2020-01-08 17:59:37 +01:00
parent 79d7335b8d
commit 621936e951
19 changed files with 294 additions and 18 deletions
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
import org.jetbrains.kotlin.config.JvmDefaultMode;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
@@ -73,6 +74,7 @@ import static org.jetbrains.kotlin.codegen.CodegenUtilKt.generateBridgeForMainFu
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.isAccessor;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION;
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
@@ -1041,17 +1043,24 @@ public class FunctionCodegen {
}
@NotNull
public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper mapper) {
return ArrayUtil.toStringArray(CollectionsKt.map(getThrownExceptions(function), d -> mapper.mapClass(d).getInternalName()));
public static String[] getThrownExceptions(@NotNull FunctionDescriptor function, @NotNull KotlinTypeMapper typeMapper) {
return ArrayUtil.toStringArray(CollectionsKt.map(
getThrownExceptions(function, typeMapper.getLanguageVersionSettings()),
d -> typeMapper.mapClass(d).getInternalName()
));
}
@NotNull
public static List<ClassDescriptor> getThrownExceptions(@NotNull FunctionDescriptor function) {
AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.throws"));
if (annotation == null) {
annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.jvm.Throws"));
public static List<ClassDescriptor> getThrownExceptions(
@NotNull FunctionDescriptor function,
@NotNull LanguageVersionSettings languageVersionSettings
) {
if (function.getKind() == DELEGATION &&
languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers)) {
return Collections.emptyList();
}
AnnotationDescriptor annotation = function.getAnnotations().findAnnotation(new FqName("kotlin.jvm.Throws"));
if (annotation == null) return Collections.emptyList();
Collection<ConstantValue<?>> values = annotation.getAllValueArguments().values();
@@ -83,7 +83,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
val bindingContext: BindingContext,
val classBuilderMode: ClassBuilderMode,
private val moduleName: String,
private val languageVersionSettings: LanguageVersionSettings,
val languageVersionSettings: LanguageVersionSettings,
private val incompatibleClassTracker: IncompatibleClassTracker = IncompatibleClassTracker.DoNothing,
val jvmTarget: JvmTarget = JvmTarget.DEFAULT,
private val isIrBackend: Boolean = false,
@@ -187,21 +187,25 @@ open class FunctionCodegen(
synchronizedFlag
}
protected open fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor {
// @Throws(vararg exceptionClasses: KClass<out Throwable>)
val exceptions = irFunction.getAnnotation(FqName("kotlin.jvm.Throws"))?.getValueArgument(0)?.let {
(it as IrVararg).elements.map { exceptionClass ->
classCodegen.typeMapper.mapType((exceptionClass as IrClassReference).classType).internalName
}.toTypedArray()
}
return classCodegen.visitor.newMethod(
protected open fun createMethod(flags: Int, signature: JvmMethodGenericSignature): MethodVisitor =
classCodegen.visitor.newMethod(
irFunction.OtherOrigin,
flags,
signature.asmMethod.name, signature.asmMethod.descriptor,
if (flags.and(Opcodes.ACC_SYNTHETIC) != 0) null else signature.genericsSignature,
exceptions
getThrownExceptions(irFunction)?.toTypedArray()
)
private fun getThrownExceptions(function: IrFunction): List<String>? {
if (state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) &&
function.origin == IrDeclarationOrigin.DELEGATED_MEMBER
) return null
// @Throws(vararg exceptionClasses: KClass<out Throwable>)
val exceptionClasses = function.getAnnotation(FqName("kotlin.jvm.Throws"))?.getValueArgument(0) ?: return null
return (exceptionClasses as IrVararg).elements.map { exceptionClass ->
classCodegen.typeMapper.mapType((exceptionClass as IrClassReference).classType).internalName
}
}
private fun generateAnnotationDefaultValueIfNeeded(methodVisitor: MethodVisitor) {
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.asJava.builder.MemberIndex
import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
import org.jetbrains.kotlin.codegen.FunctionCodegen
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.getSpecialSignatureInfo
@@ -56,7 +57,7 @@ internal abstract class KtUltraLightMethod(
val builder = KtUltraLightThrowsReferenceListBuilder(parentMethod = this)
if (methodDescriptor !== null) {
for (ex in FunctionCodegen.getThrownExceptions(methodDescriptor)) {
for (ex in FunctionCodegen.getThrownExceptions(methodDescriptor, LanguageVersionSettingsImpl.DEFAULT)) {
val psiClassType = ex.defaultType.asPsiType(support, TypeMappingMode.DEFAULT, builder) as? PsiClassType
psiClassType ?: continue
builder.addReference(psiClassType)
@@ -0,0 +1,24 @@
// !LANGUAGE: +DoNotGenerateThrowsForDelegatedKotlinMembers
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// FILE: A.kt
import java.io.IOException
interface A {
@Throws(IOException::class)
fun foo()
}
// FILE: B.kt
class B(a: A) : A by a
fun box(): String {
val method = B::class.java.declaredMethods.single { it.name == B::foo.name }
if (method.exceptionTypes.size != 0)
return "Fail: ${method.exceptionTypes.toList()}"
return "OK"
}
@@ -0,0 +1,24 @@
// !LANGUAGE: -DoNotGenerateThrowsForDelegatedKotlinMembers
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
// FILE: A.kt
import java.io.IOException
interface A {
@Throws(IOException::class)
fun foo()
}
// FILE: B.kt
class B(a: A) : A by a
fun box(): String {
val method = B::class.java.declaredMethods.single { it.name == B::foo.name }
if (method.exceptionTypes.size != 1)
return "Fail: ${method.exceptionTypes.toList()}"
return "OK"
}
@@ -0,0 +1,21 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: A.java
import java.io.IOException;
public interface A {
void foo() throws IOException;
}
// FILE: B.kt
class B(a: A) : A by a
fun box(): String {
val method = B::class.java.declaredMethods.single { it.name == B::foo.name }
if (method.exceptionTypes.size != 0)
return "Fail: ${method.exceptionTypes.toList()}"
return "OK"
}
@@ -0,0 +1,28 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: A.kt
import java.io.IOException
interface A {
@Throws(IOException::class)
@Anno
fun foo()
}
annotation class Anno
// FILE: B.kt
class B(a: A) : A by a
fun box(): String {
val method = B::class.java.declaredMethods.single { it.name == B::foo.name }
if (method.exceptionTypes.size != 0)
return "Fail throws: ${method.exceptionTypes.toList()}"
if (method.declaredAnnotations.size != 1)
return "Fail annotations: ${method.declaredAnnotations.toList()}"
return "OK"
}
@@ -1034,6 +1034,24 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxAga
}
}
@TestMetadata("compiler/testData/codegen/boxAgainstJava/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractBlackBoxAgainstJavaCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxAgainstJava/throws"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/throws/delegationAndThrows.kt");
}
}
@TestMetadata("compiler/testData/codegen/boxAgainstJava/typealias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -27658,6 +27658,29 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractBlackBoxCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows.kt");
}
@TestMetadata("delegationAndThrows_1_3.kt")
public void testDelegationAndThrows_1_3() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows_1_3.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -123,6 +123,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl
runTest("compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt");
}
@TestMetadata("delegationAndAnnotations.kt")
public void testDelegationAndAnnotations() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/delegationAndAnnotations.kt");
}
@TestMetadata("doublyNestedClass.kt")
public void testDoublyNestedClass() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/doublyNestedClass.kt");
@@ -26475,6 +26475,29 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractLightAnalysisModeTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows.kt");
}
@TestMetadata("delegationAndThrows_1_3.kt")
public void testDelegationAndThrows_1_3() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows_1_3.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -26157,6 +26157,29 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractFirBlackBoxCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTestWithCustomIgnoreDirective(this::doTest, TargetBackend.JVM_IR, testDataFilePath, "// IGNORE_BACKEND_FIR: ");
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows.kt");
}
@TestMetadata("delegationAndThrows_1_3.kt")
public void testDelegationAndThrows_1_3() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows_1_3.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1035,6 +1035,24 @@ public class IrBlackBoxAgainstJavaCodegenTestGenerated extends AbstractIrBlackBo
}
}
@TestMetadata("compiler/testData/codegen/boxAgainstJava/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractIrBlackBoxAgainstJavaCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxAgainstJava/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/boxAgainstJava/throws/delegationAndThrows.kt");
}
}
@TestMetadata("compiler/testData/codegen/boxAgainstJava/typealias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -26157,6 +26157,29 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractIrBlackBoxCodegenTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("delegationAndThrows.kt")
public void testDelegationAndThrows() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows.kt");
}
@TestMetadata("delegationAndThrows_1_3.kt")
public void testDelegationAndThrows_1_3() throws Exception {
runTest("compiler/testData/codegen/box/throws/delegationAndThrows_1_3.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -118,6 +118,11 @@ public class IrCompileKotlinAgainstKotlinTestGenerated extends AbstractIrCompile
runTest("compiler/testData/compileKotlinAgainstKotlin/delegatedDefault.kt");
}
@TestMetadata("delegationAndAnnotations.kt")
public void testDelegationAndAnnotations() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/delegationAndAnnotations.kt");
}
@TestMetadata("doublyNestedClass.kt")
public void testDoublyNestedClass() throws Exception {
runTest("compiler/testData/compileKotlinAgainstKotlin/doublyNestedClass.kt");
@@ -124,6 +124,7 @@ enum class LanguageFeature(
AllowContractsForNonOverridableMembers(KOTLIN_1_4),
AllowReifiedGenericsInContracts(KOTLIN_1_4),
ProperVisibilityForCompanionObjectInstanceField(KOTLIN_1_4, kind = BUG_FIX),
DoNotGenerateThrowsForDelegatedKotlinMembers(KOTLIN_1_4),
// Temporarily disabled, see KT-27084/KT-22379
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
@@ -21318,6 +21318,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractIrJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -22453,6 +22453,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/throws")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Throws extends AbstractJsCodegenBoxTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
}
public void testAllFilesPresentInThrows() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/throws"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
}
@TestMetadata("compiler/testData/codegen/box/toArray")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)