diff --git a/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.kt b/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.kt new file mode 100644 index 00000000000..1c5ad8b7ca9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.kt @@ -0,0 +1,38 @@ +target(AnnotationTarget.PROPERTY) +annotation class AnnProperty + +target(AnnotationTarget.FIELD) +annotation class AnnField + +target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY) +annotation class AnnFieldProperty + +target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY) +annotation class AnnParameterProperty + +target(AnnotationTarget.PROPERTY, AnnotationTarget.FIELD) +annotation class AnnParameterField + +target(AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) +annotation class AnnGetterSetter + +target(AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.PROPERTY) +annotation class AnnPropertySetter + +target(AnnotationTarget.PROPERTY, AnnotationTarget.PROPERTY_GETTER) +annotation class AnnTypeGetter + +target(AnnotationTarget.PROPERTY, AnnotationTarget.TYPE) +annotation class AnnTypeField + +public class A( + @AnnProperty @AnnField @AnnFieldProperty @AnnParameterProperty @AnnParameterField + @AnnGetterSetter @AnnPropertySetter @AnnTypeGetter @AnnTypeField + public val x: Int +) { + + @AnnProperty @AnnField @AnnFieldProperty @AnnParameterProperty @AnnParameterField + @AnnGetterSetter @AnnPropertySetter @AnnTypeGetter @AnnTypeField + public val a: Int = 1 + +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.txt b/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.txt new file mode 100644 index 00000000000..61ed5f898f9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.txt @@ -0,0 +1,29 @@ +@kotlin.jvm.internal.KotlinClass A { + field $kotlinClass: kotlin.reflect.KClass + @AnnField @AnnFieldProperty @AnnParameterField field a: int + @AnnField @AnnFieldProperty @AnnParameterField field x: int + method (): void + @AnnProperty @AnnParameterProperty @AnnPropertySetter @AnnTypeGetter @AnnTypeField method a$annotations(): void + @AnnGetterSetter method getA(): int + @AnnProperty @AnnPropertySetter @AnnTypeGetter @AnnTypeField method x$annotations(): void + @AnnGetterSetter method getX(): int + method (@AnnParameterProperty p0: int): void +} + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnField + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnFieldProperty + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnGetterSetter + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnParameterField + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnParameterProperty + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnProperty + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnPropertySetter + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnTypeField + +@kotlin.annotation.target @java.lang.annotation.Retention @java.lang.annotation.Target @kotlin.jvm.internal.KotlinClass AnnTypeGetter \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/annotations/onProperties.kt b/compiler/testData/codegen/bytecodeListing/annotations/onProperties.kt new file mode 100644 index 00000000000..1486bf2c799 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/onProperties.kt @@ -0,0 +1,13 @@ +annotation class AnnProp +annotation class AnnField +annotation class AnnProp2 +annotation class AnnGetter +annotation class AnnSetter +annotation class AnnParam + +public class A(@AnnParam @field:AnnField @property:AnnProp2 val x: Int, @param:AnnParam @get:AnnGetter @set:AnnSetter var y: Int) { + + @AnnProp @field:AnnField @property:AnnProp2 @get:AnnGetter @set:AnnSetter @sparam:AnnParam + var p: Int = 0 + +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/annotations/onProperties.txt b/compiler/testData/codegen/bytecodeListing/annotations/onProperties.txt new file mode 100644 index 00000000000..98d92bb195f --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/onProperties.txt @@ -0,0 +1,27 @@ +@kotlin.jvm.internal.KotlinClass A { + field $kotlinClass: kotlin.reflect.KClass + @AnnProp @AnnField field p: int + @AnnField field x: int + field y: int + method (): void + @AnnProp2 method p$annotations(): void + @AnnGetter method getP(): int + @AnnSetter method setP(@AnnParam p0: int): void + @AnnProp2 method x$annotations(): void + method getX(): int + @AnnGetter method getY(): int + @AnnSetter method setY(p0: int): void + method (@AnnParam p0: int, @AnnParam p1: int): void +} + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnField + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnGetter + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnParam + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnProp + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnProp2 + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass AnnSetter \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.kt b/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.kt new file mode 100644 index 00000000000..d33f698dbdb --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.kt @@ -0,0 +1,14 @@ +public annotation class Ann + +public class A { + fun @receiver:Ann String.f(): String = "" + + val @receiver:Ann String.p: String + get() = "" + +} + +fun @receiver:Ann String.topLevelF(): String = "" + +val @receiver:Ann String.topLevelP: String + get() = "" \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.txt b/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.txt new file mode 100644 index 00000000000..eb2b7bb6013 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/annotations/onReceiver.txt @@ -0,0 +1,21 @@ +@kotlin.jvm.internal.KotlinClass A { + field $kotlinClass: kotlin.reflect.KClass + method (): void + @org.jetbrains.annotations.NotNull method f(@Ann p0: java.lang.String): java.lang.String + @org.jetbrains.annotations.NotNull method getP(@Ann p0: java.lang.String): java.lang.String + method (): void +} + +@java.lang.annotation.Retention @kotlin.jvm.internal.KotlinClass Ann + +@kotlin.jvm.internal.KotlinSyntheticClass _DefaultPackage$onReceiver$b389baba { + @org.jetbrains.annotations.NotNull method topLevelF(@Ann p0: java.lang.String): java.lang.String + @org.jetbrains.annotations.NotNull method getTopLevelP(@Ann p0: java.lang.String): java.lang.String +} + +@kotlin.jvm.internal.KotlinPackage _DefaultPackage { + field $kotlinPackage: kotlin.reflect.KPackage + method (): void + @org.jetbrains.annotations.NotNull method getTopLevelP(@Ann p0: java.lang.String): java.lang.String + @org.jetbrains.annotations.NotNull method topLevelF(@Ann p0: java.lang.String): java.lang.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt new file mode 100644 index 00000000000..e13c2ee30fd --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.test.ConfigurationKind +import org.jetbrains.kotlin.test.JetTestUtils +import org.jetbrains.org.objectweb.asm.* +import java.io.File +import java.util.* + +public abstract class AbstractBytecodeListingTest : CodegenTestCase() { + + throws(Exception::class) + public fun doTest(filename: String) { + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL) + loadFileByFullPath(filename) + val ktFile = File(filename) + val txtFile = File(ktFile.parent, ktFile.nameWithoutExtension + ".txt") + val generatedFiles = CodegenTestUtil.generateFiles(myEnvironment, myFiles) + .asList() + .sortedBy { it.relativePath } + .map { + val cr = ClassReader(it.asByteArray()) + val visitor = TextCollectingVisitor() + cr.accept(visitor, ClassReader.SKIP_CODE) + visitor.text + }.joinToString("\n\n") + + JetTestUtils.assertEqualsToFile(txtFile, generatedFiles) + } + + private class TextCollectingVisitor : ClassVisitor(Opcodes.ASM5) { + private class Declaration(val text: String, val annotations: MutableList = arrayListOf()) + + private val declarationsInsideClass = arrayListOf() + private val classAnnotations = arrayListOf() + private var className = "" + + private fun addAnnotation(desc: String) { + val name = Type.getType(desc).className + declarationsInsideClass.last().annotations.add("@$name ") + } + + public val text: String + get() = StringBuilder { + append(classAnnotations.joinToString("")) + append(className) + if (declarationsInsideClass.isNotEmpty()) { + append(" {\n") + for (declaration in declarationsInsideClass) { + append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n") + } + append("}") + } + }.toString() + + override fun visitMethod( + access: Int, + name: String, + desc: String, + signature: String?, + exceptions: Array? + ): MethodVisitor? { + val returnType = Type.getReturnType(desc).className + val parameterTypes = Type.getArgumentTypes(desc).map { it.className } + val methodAnnotations = arrayListOf() + val parameterAnnotations = hashMapOf>() + + return object : MethodVisitor(Opcodes.ASM5) { + override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { + val type = Type.getType(desc).className + methodAnnotations += "@$type " + return super.visitAnnotation(desc, visible) + } + + override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? { + val type = Type.getType(desc).className + parameterAnnotations.getOrPut(parameter, { arrayListOf() }).add("@$type ") + return super.visitParameterAnnotation(parameter, desc, visible) + } + + override fun visitEnd() { + val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter -> + val annotations = parameterAnnotations.getOrElse(index, { emptyList() }).joinToString("") + "${annotations}p$index: $parameter" + }.joinToString() + declarationsInsideClass.add(Declaration("method $name($parameterWithAnnotations): $returnType", methodAnnotations)) + super.visitEnd() + } + } + } + + override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? { + val type = Type.getType(desc).className + declarationsInsideClass.add(Declaration("field $name: $type")) + return object : FieldVisitor(Opcodes.ASM5) { + override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { + addAnnotation(desc) + return super.visitAnnotation(desc, visible) + } + } + } + + override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? { + val name = Type.getType(desc).className + classAnnotations.add("@$name ") + return super.visitAnnotation(desc, visible) + } + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array? + ) { + className = name + } + + override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) { + declarationsInsideClass.add(Declaration("inner class $name")) + } + } + +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java new file mode 100644 index 00000000000..5a6164227e7 --- /dev/null +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.JetTestUtils; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/codegen/bytecodeListing") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { + public void testAllFilesPresentInBytecodeListing() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("compiler/testData/codegen/bytecodeListing/annotations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Annotations extends AbstractBytecodeListingTest { + public void testAllFilesPresentInAnnotations() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/annotations"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("defaultTargets.kt") + public void testDefaultTargets() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/annotations/defaultTargets.kt"); + doTest(fileName); + } + + @TestMetadata("onProperties.kt") + public void testOnProperties() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/annotations/onProperties.kt"); + doTest(fileName); + } + + @TestMetadata("onReceiver.kt") + public void testOnReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/annotations/onReceiver.kt"); + doTest(fileName); + } + } +} diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 0aba493d907..4a337c71f6d 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -208,6 +208,10 @@ fun main(args: Array) { model("codegen/bytecodeText") } + testClass(javaClass()) { + model("codegen/bytecodeListing") + } + testClass(javaClass()) { model("codegen/topLevelMemberInvocation", extension = null, recursive = false) }