diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index 752f2eb662d..8eb0dcca9fb 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -19,6 +19,8 @@ package org.jetbrains.jet.codegen; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; @@ -28,11 +30,15 @@ import org.jetbrains.jet.codegen.context.CodegenContext; import org.jetbrains.jet.codegen.context.PackageContext; import org.jetbrains.jet.codegen.state.JetTypeMapper; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.Annotated; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.Annotations; import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil; +import org.jetbrains.jet.lang.resolve.constants.*; +import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeUtils; @@ -253,4 +259,34 @@ public class CodegenUtil { return null; } + + @NotNull + public static String[] getExceptions(@NotNull Annotated annotatedDescriptor, @NotNull final JetTypeMapper mapper) { + // Can't say 'throws.class', because 'throws' is a reserved work in Java + AnnotationDescriptor annotation = annotatedDescriptor.getAnnotations().findAnnotation(new FqName("kotlin.throws")); + if (annotation == null) return ArrayUtil.EMPTY_STRING_ARRAY; + + Collection> values = annotation.getAllValueArguments().values(); + if (values.isEmpty()) return ArrayUtil.EMPTY_STRING_ARRAY; + + Object value = values.iterator().next(); + if (!(value instanceof ArrayValue)) return ArrayUtil.EMPTY_STRING_ARRAY; + ArrayValue arrayValue = (ArrayValue) value; + + List strings = ContainerUtil.mapNotNull( + arrayValue.getValue(), + new Function, String>() { + @Override + public String fun(CompileTimeConstant constant) { + if (constant instanceof JavaClassValue) { + JavaClassValue classValue = (JavaClassValue) constant; + ClassDescriptor classDescriptor = DescriptorUtils.getClassDescriptorForType(classValue.getValue()); + return mapper.mapClass(classDescriptor).getInternalName(); + } + return null; + } + } + ); + return strings.toArray(new String[strings.size()]); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java index f8da9631918..b93dca378f0 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -109,7 +109,7 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { asmMethod.getName(), asmMethod.getDescriptor(), jvmSignature.getGenericsSignature(), - null); + CodegenUtil.getExceptions(functionDescriptor, typeMapper)); if (owner instanceof PackageFacadeContext) { Type ownerType = ((PackageFacadeContext) owner).getDelegateToClassType(); @@ -462,7 +462,8 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { return; } int flags = getVisibilityAccessFlag(constructorDescriptor); - MethodVisitor mv = classBuilder.newMethod(null, flags, "", "()V", null, null); + MethodVisitor mv = classBuilder.newMethod(null, flags, "", "()V", null, + CodegenUtil.getExceptions(constructorDescriptor, state.getTypeMapper())); if (state.getClassBuilderMode() == ClassBuilderMode.LIGHT_CLASSES) return; @@ -531,7 +532,8 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { } MethodVisitor mv = v.newMethod(null, flags | (isConstructor ? 0 : ACC_STATIC), isConstructor ? "" : jvmSignature.getName() + JvmAbi.DEFAULT_PARAMS_IMPL_SUFFIX, - descriptor, null, null); + descriptor, null, + CodegenUtil.getExceptions(functionDescriptor, typeMapper)); if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { generateDefaultImpl(owner, signature, functionDescriptor, isStatic, mv, loadStrategy); @@ -739,7 +741,8 @@ public class FunctionCodegen extends ParentCodegenAwareImpl { int flags = ACC_PUBLIC; - MethodVisitor mv = v.newMethod(null, flags, delegateMethod.getName(), delegateMethod.getDescriptor(), null, null); + MethodVisitor mv = v.newMethod(null, flags, delegateMethod.getName(), delegateMethod.getDescriptor(), null, + CodegenUtil.getExceptions(functionDescriptor, typeMapper)); if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; mv.visitCode(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 75edeb4cc98..5bd5e97e93e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -1425,7 +1425,8 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { Method methodInTrait = typeMapper.mapSignature(fun.getOriginal()).getAsmMethod(); PsiElement origin = descriptorToDeclaration(bindingContext, fun); - MethodVisitor mv = v.newMethod(origin, flags, methodToGenerate.getName(), methodToGenerate.getDescriptor(), null, null); + MethodVisitor mv = v.newMethod(origin, flags, methodToGenerate.getName(), methodToGenerate.getDescriptor(), null, + CodegenUtil.getExceptions(fun, typeMapper)); AnnotationCodegen.forMethod(mv, typeMapper).genAnnotations(fun); if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.java new file mode 100644 index 00000000000..5abf1f4e671 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.java @@ -0,0 +1,17 @@ +class JavaClass { + void testMethod() { + Test test = new Test(); + test.none(); + + try { + test.one(); + } + catch (E1 e) {} + + try { + test.two(); + } + catch (E1 e) {} + catch (E2 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.kt new file mode 100644 index 00000000000..03fd4dd27ab --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.kt @@ -0,0 +1,13 @@ +class E1: Exception() +class E2: Exception() + +class Test { + throws() + fun none() {} + + throws(javaClass()) + fun one() {} + + throws(javaClass(), javaClass()) + fun two() {} +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.java new file mode 100644 index 00000000000..111e4b136b5 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.java @@ -0,0 +1,21 @@ +class JavaClass { + void testMethod() { + new None(); + + try { + new One(); + } + catch (E1 e) {} + + try { + new OneWithParam(1); + } + catch (E1 e) {} + + try { + new Two(); + } + catch (E1 e) {} + catch (E2 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.kt new file mode 100644 index 00000000000..a0efb8dcd14 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.kt @@ -0,0 +1,8 @@ +class E1: Exception() +class E2: Exception() + +class None [throws()]() {} +class One [throws(javaClass())]() +class Two [throws(javaClass(), javaClass())]() + +class OneWithParam [throws(javaClass())](a: Int) \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java new file mode 100644 index 00000000000..6b4397edb3b --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.java @@ -0,0 +1,33 @@ +class JavaClass { + void testMethod(One instance) { + try { + new One(1); + } + catch (E1 e) {} + + try { + new One(1, 0); + } + catch (E1 e) {} + + try { + new One(); + } + catch (E1 e) {} + + try { + One.one$default(instance, 1, 1); + } + catch (E1 e) {} + + try { + _DefaultPackage.one(1); + } + catch (E1 e) {} + + try { + _DefaultPackage.one$default(1, 0); + } + catch (E1 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.kt new file mode 100644 index 00000000000..b1354708195 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.kt @@ -0,0 +1,9 @@ +class E1: Exception() + +throws(javaClass()) +fun one(a: Int = 1) {} + +class One [throws(javaClass())] (a: Int = 1) { + throws(javaClass()) + fun one(a: Int = 1) {} +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.java new file mode 100644 index 00000000000..5abf1f4e671 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.java @@ -0,0 +1,17 @@ +class JavaClass { + void testMethod() { + Test test = new Test(); + test.none(); + + try { + test.one(); + } + catch (E1 e) {} + + try { + test.two(); + } + catch (E1 e) {} + catch (E2 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.kt new file mode 100644 index 00000000000..4e2c3cc1643 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.kt @@ -0,0 +1,21 @@ +class E1: Exception() +class E2: Exception() + +trait Trait { + throws() + fun none() + + throws(javaClass()) + fun one() + + throws(javaClass(), javaClass()) + fun two() +} + +class Impl: Trait { + override fun none() {} + override fun one() {} + override fun two() {} +} + +class Test: Trait by Impl() \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.java new file mode 100644 index 00000000000..1a8888bfa2d --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.java @@ -0,0 +1,16 @@ +class JavaClass { + void testMethod() { + _DefaultPackage.none(); + + try { + _DefaultPackage.one(); + } + catch (E1 e) {} + + try { + _DefaultPackage.two(); + } + catch (E1 e) {} + catch (E2 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.kt new file mode 100644 index 00000000000..fd73d8149df --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.kt @@ -0,0 +1,11 @@ +class E1: Exception() +class E2: Exception() + +throws() +fun none() {} + +throws(javaClass()) +fun one() {} + +throws(javaClass(), javaClass()) +fun two() {} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.java b/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.java new file mode 100644 index 00000000000..5abf1f4e671 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.java @@ -0,0 +1,17 @@ +class JavaClass { + void testMethod() { + Test test = new Test(); + test.none(); + + try { + test.one(); + } + catch (E1 e) {} + + try { + test.two(); + } + catch (E1 e) {} + catch (E2 e) {} + } +} \ No newline at end of file diff --git a/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.kt b/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.kt new file mode 100644 index 00000000000..030071781d3 --- /dev/null +++ b/compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.kt @@ -0,0 +1,15 @@ +class E1: Exception() +class E2: Exception() + +trait Trait { + throws() + fun none() {} + + throws(javaClass()) + fun one() {} + + throws(javaClass(), javaClass()) + fun two() {} +} + +class Test: Trait \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileJavaAgainstKotlinTest.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileJavaAgainstKotlinTest.java index 5cfc8970739..e2075f02672 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileJavaAgainstKotlinTest.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/AbstractCompileJavaAgainstKotlinTest.java @@ -35,7 +35,7 @@ public abstract class AbstractCompileJavaAgainstKotlinTest extends TestCaseWithT Assert.assertTrue(ktFilePath.endsWith(".kt")); File ktFile = new File(ktFilePath); File javaFile = new File(ktFilePath.replaceFirst("\\.kt", ".java")); - compileKotlinToDirAndGetAnalyzeExhaust(Collections.singletonList(ktFile), tmpdir, getTestRootDisposable(), ConfigurationKind.JDK_ONLY); + compileKotlinToDirAndGetAnalyzeExhaust(Collections.singletonList(ktFile), tmpdir, getTestRootDisposable(), ConfigurationKind.ALL); List options = Arrays.asList( "-classpath", tmpdir.getPath() + System.getProperty("path.separator") + ForTestCompileRuntime.runtimeJarForTests(), diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java index 2c8a8c10e9d..8f3bedb8726 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileJavaAgainstKotlinTestGenerated.java @@ -91,7 +91,7 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg } @TestMetadata("compiler/testData/compileJavaAgainstKotlin/method") - @InnerTestClasses({Method.PrimitiveOverride.class}) + @InnerTestClasses({Method.PrimitiveOverride.class, Method.Throws.class}) public static class Method extends AbstractCompileJavaAgainstKotlinTest { public void testAllFilesPresentInMethod() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/compileJavaAgainstKotlin/method"), Pattern.compile("^(.+)\\.kt$"), true); @@ -260,10 +260,49 @@ public class CompileJavaAgainstKotlinTestGenerated extends AbstractCompileJavaAg } + @TestMetadata("compiler/testData/compileJavaAgainstKotlin/method/throws") + public static class Throws extends AbstractCompileJavaAgainstKotlinTest { + public void testAllFilesPresentInThrows() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/compileJavaAgainstKotlin/method/throws"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ClassMembers.kt") + public void testClassMembers() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/ClassMembers.kt"); + } + + @TestMetadata("Constructor.kt") + public void testConstructor() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/Constructor.kt"); + } + + @TestMetadata("DefaultArgs.kt") + public void testDefaultArgs() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/DefaultArgs.kt"); + } + + @TestMetadata("Delegation.kt") + public void testDelegation() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/Delegation.kt"); + } + + @TestMetadata("TopLevel.kt") + public void testTopLevel() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/TopLevel.kt"); + } + + @TestMetadata("TraitMembers.kt") + public void testTraitMembers() throws Exception { + doTest("compiler/testData/compileJavaAgainstKotlin/method/throws/TraitMembers.kt"); + } + + } + public static Test innerSuite() { TestSuite suite = new TestSuite("Method"); suite.addTestSuite(Method.class); suite.addTestSuite(PrimitiveOverride.class); + suite.addTestSuite(Throws.class); return suite; } }