diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 03a5995be65..cccac38c98b 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -149,8 +149,8 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { @Argument( value = "-Xuse-old-class-files-reading", - description = "Use old class files reading implementation " + - "(may slow down the build and should be used in case of problems with the new implementation)" + description = "Use old class files reading implementation. This may slow down the build and cause problems with Groovy interop.\n" + + "Should be used in case of problems with the new implementation" ) var useOldClassFilesReading: Boolean by FreezableVar(false) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt index 2dbcc1bb1bd..8889cf142d0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -50,8 +50,11 @@ class BinaryJavaClass( override val annotationsByFqName by buildLazyValueForMap() - private val innerClassNameToAccess: MutableMap = THashMap() - override val innerClassNames get() = innerClassNameToAccess.keys + // Short name of a nested class of this class -> access flags as seen in the InnerClasses attribute value. + // Note that it doesn't include classes mentioned in other InnerClasses attribute values (those which are not nested in this class). + private val ownInnerClassNameToAccess: MutableMap = THashMap() + + override val innerClassNames get() = ownInnerClassNameToAccess.keys override val name: Name get() = fqName.shortName() @@ -100,9 +103,14 @@ class BinaryJavaClass( if (access.isSet(Opcodes.ACC_SYNTHETIC)) return if (innerName == null || outerName == null) return - if (myInternalName == outerName) { + // Do not read InnerClasses attribute values where full name != outer + $ + inner; treat those classes as top level instead. + // This is possible for example for Groovy-generated $Trait$FieldHelper classes. + if (name == "$outerName$$innerName") { context.addInnerClass(name, outerName, innerName) - innerClassNameToAccess[context.mapInternalNameToClassId(name).shortClassName] = access + + if (myInternalName == outerName) { + ownInnerClassNameToAccess[context.mapInternalNameToClassId(name).shortClassName] = access + } } } @@ -195,7 +203,7 @@ class BinaryJavaClass( BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser) override fun findInnerClass(name: Name): JavaClass? { - val access = innerClassNameToAccess[name] ?: return null + val access = ownInnerClassNameToAccess[name] ?: return null return virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let { BinaryJavaClass(it, fqName.child(name), context.copyForMember(), signatureParser, access, this) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt index 97948dabe2b..cf322afacf6 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.load.java.structure.impl.classFiles -import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.JavaClassifier import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter @@ -78,45 +77,17 @@ class ClassifierResolutionContext private constructor( } if ('$' in internalName) { - val innerClassInfo = innerClasses.getOrNull(internalName) ?: return mapInternalNameToClassIdNaively(internalName) - if (Name.isValidIdentifier(innerClassInfo.simpleName)) { + val innerClassInfo = innerClasses.getOrNull(internalName) + if (innerClassInfo != null && Name.isValidIdentifier(innerClassInfo.simpleName)) { val outerClassId = mapInternalNameToClassId(innerClassInfo.outerInternalName) return outerClassId.createNestedClassId(Name.identifier(innerClassInfo.simpleName)) } } - return createClassIdForTopLevel(internalName) + return ClassId.topLevel(FqName(internalName.replace('/', '.'))) } - // See com.intellij.psi.impl.compiled.StubBuildingVisitor.GUESSING_MAPPER - private fun mapInternalNameToClassIdNaively(internalName: String): ClassId { - val splitPoints = ContainerUtil.newSmartList() - for (p in 0..internalName.length - 1) { - val c = internalName[p] - if (c == '$' && p > 0 && internalName[p - 1] != '/' && p < internalName.length - 1 && internalName[p + 1] != '$') { - splitPoints.add(p) - } - } - - if (splitPoints.isNotEmpty()) { - val substrings = (listOf(-1) + splitPoints).zip(splitPoints + internalName.length).map { (from, to) -> - internalName.substring(from + 1, to) - } - - val outerFqName = FqName(substrings[0].replace('/', '.')) - val packageFqName = outerFqName.parent() - val relativeName = - FqName(outerFqName.shortName().asString() + "." + substrings.subList(1, substrings.size).joinToString(".")) - - return ClassId(packageFqName, relativeName, false) - } - - return createClassIdForTopLevel(internalName) - } - - private fun createClassIdForTopLevel(internalName: String) = ClassId.topLevel(FqName(internalName.replace('/', '.'))) - - internal fun resolveByInternalName(c: String) = resolveClass(mapInternalNameToClassId(c)) + internal fun resolveByInternalName(c: String): Result = resolveClass(mapInternalNameToClassId(c)) internal fun mapDescToClassId(desc: String): ClassId = mapInternalNameToClassId(Type.getType(desc).internalName) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Other.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Other.kt index 071a736711c..2e43b0ba6be 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Other.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Other.kt @@ -67,24 +67,27 @@ class BinaryJavaValueParameter( fun isNotTopLevelClass(classContent: ByteArray): Boolean { var isNotTopLevelClass = false ClassReader(classContent).accept( - object : ClassVisitor(ASM_API_VERSION_FOR_CLASS_READING) { - private var internalName: String? = null - override fun visit( - version: Int, - access: Int, - name: String?, - signature: String?, - superName: String?, - interfaces: Array? - ) { - internalName = name - } + object : ClassVisitor(ASM_API_VERSION_FOR_CLASS_READING) { + private var internalName: String? = null + override fun visit( + version: Int, + access: Int, + name: String?, + signature: String?, + superName: String?, + interfaces: Array? + ) { + internalName = name + } - override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) { - isNotTopLevelClass = isNotTopLevelClass or (name == internalName) + override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) { + // Do not read InnerClasses attribute values where full name != outer + $ + inner; treat those classes as top level instead. + if (name == internalName && (innerName == null || name == "$outerName$$innerName")) { + isNotTopLevelClass = true } - }, - ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES + } + }, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES ) return isNotTopLevelClass } diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index 0ab2579802e..6c5ee97354e 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -61,7 +61,8 @@ where advanced options include: Default value is 'enable' -Xuse-ir Use the IR backend -Xuse-javac Use javac for Java source and class files analysis - -Xuse-old-class-files-reading Use old class files reading implementation (may slow down the build and should be used in case of problems with the new implementation) + -Xuse-old-class-files-reading Use old class files reading implementation. This may slow down the build and cause problems with Groovy interop. + Should be used in case of problems with the new implementation -Xuse-type-table Use type table in metadata serialization -Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info -Xallow-result-return-type Allow compiling code when `kotlin.Result` is used as a return type diff --git a/compiler/testData/loadJava/compiledJava/TopLevel$Class.fast.txt b/compiler/testData/loadJava/compiledJava/TopLevel$Class.fast.txt new file mode 100644 index 00000000000..eb2c9438366 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/TopLevel$Class.fast.txt @@ -0,0 +1,6 @@ +package test + +public open class `TopLevel$Class` { + public constructor `TopLevel$Class`() + public open fun foo(/*0*/ p0: test.`TopLevel$Class`!): kotlin.Unit +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt index 3d87ded8122..d8d7e16c863 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt @@ -107,4 +107,11 @@ class SimpleKotlinGradleIT : BaseGradleIT() { assertNoSuchFile("build") } } -} \ No newline at end of file + + @Test + fun testGroovyTraitsWithFields() { + Project("groovyTraitsWithFields").build("build") { + assertSuccessful() + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/build.gradle new file mode 100644 index 00000000000..18ecef3b403 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/build.gradle @@ -0,0 +1,34 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: "kotlin" +apply plugin: "groovy" +apply plugin: "java" + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + compile 'org.codehaus.groovy:groovy-all:2.5.3' +} + +compileGroovy.dependsOn = compileGroovy.taskDependencies.values - 'compileJava' + +compileKotlin { + dependsOn compileGroovy + classpath += files(compileGroovy.destinationDir) +} + +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTrait.groovy b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTrait.groovy new file mode 100644 index 00000000000..7b535c59ee7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTrait.groovy @@ -0,0 +1,11 @@ +import groovy.transform.CompileStatic + +@CompileStatic +trait MyTrait { + private transient boolean somePrivateField = false + List someField + + def foo() { + return 1 + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTraitAccessor.groovy b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTraitAccessor.groovy new file mode 100644 index 00000000000..d2951c8253f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/groovy/MyTraitAccessor.groovy @@ -0,0 +1,3 @@ +class MyTraitAccessor implements MyTrait { + def myField = 1 +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt new file mode 100644 index 00000000000..0f51db3c229 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/groovyTraitsWithFields/src/main/kotlin/MyKotlinClass.kt @@ -0,0 +1,3 @@ +fun main(args: Array) { + System.out.println(MyTraitAccessor().myField) +}