diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt index 90db7b65fa5..fb096f8f81b 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCliJavaFileManagerImpl.kt @@ -28,6 +28,10 @@ import org.jetbrains.kotlin.cli.jvm.index.JavaRoot import org.jetbrains.kotlin.cli.jvm.index.JvmDependenciesIndex import org.jetbrains.kotlin.load.java.structure.JavaClass import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.isNotTopLevelClass import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager @@ -42,6 +46,7 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ private val perfCounter = PerformanceCounter.create("Find Java class") private var index: JvmDependenciesIndex by Delegates.notNull() private val topLevelClassesCache: MutableMap = THashMap() + private val allScope = GlobalSearchScope.allScope(myPsiManager.project) fun initIndex(packagesCache: JvmDependenciesIndex) { this.index = packagesCache @@ -60,8 +65,41 @@ class KotlinCliJavaFileManagerImpl(private val myPsiManager: PsiManager) : CoreJ }?.takeIf { it in searchScope } } + private val binaryCache: MutableMap = THashMap() + private val signatureParsingComponent = + BinaryClassSignatureParser(ClassifierResolutionContext { findClass(it, allScope) }) + override fun findClass(classId: ClassId, searchScope: GlobalSearchScope): JavaClass? { val virtualFile = findVirtualFileForTopLevelClass(classId, searchScope) ?: return null + + if (virtualFile.extension == "class") { + // We return all class files' names in the directory in knownClassNamesInPackage method, so one may request an inner class + return binaryCache.getOrPut(classId) { + // Note that currently we implicitly suppose that searchScope for binary classes is constant and we do not use it + // as a key in cache + // This is a true assumption by now since there are two search scopes in compiler: one for sources and another one for binary + // When it become wrong because we introduce the modules into CLI, it's worth to consider + // having different KotlinCliJavaFileManagerImpl's for different modules + val classContent = virtualFile.contentsToByteArray() + if (virtualFile.nameWithoutExtension.contains("$") && isNotTopLevelClass(classContent)) return@getOrPut null + classId.outerClassId?.let { outerClassId -> + val outerClass = findClass(outerClassId, searchScope) + return@getOrPut outerClass?.findInnerClass(classId.shortClassName) + } + + val resolver = ClassifierResolutionContext { findClass(it, searchScope) } + + BinaryJavaClass( + virtualFile, + classId.asSingleFqName(), + resolver, + signatureParsingComponent, + outerClass = null, + classContent = classContent + ) + } + } + return virtualFile.findPsiClassInVirtualFile(classId.relativeClassName.asString())?.let(::JavaClassImpl) } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt new file mode 100644 index 00000000000..28ad4c8cd4d --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Annotations.kt @@ -0,0 +1,203 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jetbrains.org.objectweb.asm.AnnotationVisitor +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Type +import java.lang.reflect.Array + +internal class AnnotationsCollectorMethodVisitor( + private val member: BinaryJavaMethodBase, + private val context: ClassifierResolutionContext, + private val signatureParser: BinaryClassSignatureParser, + private val parametersToSkipNumber: Int +) : MethodVisitor(ASM_API_VERSION_FOR_CLASS_READING) { + override fun visitAnnotationDefault(): AnnotationVisitor? { + member.safeAs()?.hasAnnotationParameterDefaultValue = true + // We don't store default value in Java model + return null + } + + override fun visitAnnotation(desc: String, visible: Boolean) = + BinaryJavaAnnotation.addAnnotation( + member.annotations as MutableCollection, + desc, context, signatureParser + ) + + override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? { + val index = parameter - parametersToSkipNumber + if (index < 0) return null + + val annotations = + member.valueParameters[index].annotations as MutableCollection? + ?: return null + + return BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser) + } +} + +class BinaryJavaAnnotation private constructor( + desc: String, + private val context: ClassifierResolutionContext, + override val arguments: Collection +) : JavaAnnotation { + + companion object { + + fun createAnnotationAndVisitor( + desc: String, + context: ClassifierResolutionContext, + signatureParser: BinaryClassSignatureParser + ): Pair { + val arguments = mutableListOf() + val annotation = BinaryJavaAnnotation(desc, context, arguments) + + return annotation to BinaryJavaAnnotationVisitor(context, signatureParser, arguments) + } + + fun addAnnotation( + annotations: MutableCollection, + desc: String, + context: ClassifierResolutionContext, + signatureParser: BinaryClassSignatureParser + ): AnnotationVisitor { + val (javaAnnotation, annotationVisitor) = createAnnotationAndVisitor(desc, context, signatureParser) + annotations.add(javaAnnotation) + + return annotationVisitor + } + } + + private val classifierResolutionResult by lazy(LazyThreadSafetyMode.NONE) { + context.resolveByInternalName(Type.getType(desc).internalName) + } + + override val classId: ClassId? + get() = classifierResolutionResult.classifier.safeAs()?.classId() + ?: ClassId.topLevel(FqName(classifierResolutionResult.qualifiedName)) + + override fun resolve() = classifierResolutionResult.classifier as? JavaClass +} + +class BinaryJavaAnnotationVisitor( + private val context: ClassifierResolutionContext, + private val signatureParser: BinaryClassSignatureParser, + private val arguments: MutableCollection +) : AnnotationVisitor(ASM_API_VERSION_FOR_CLASS_READING) { + private fun addArgument(argument: JavaAnnotationArgument?) { + arguments.addIfNotNull(argument) + } + + override fun visitAnnotation(name: String?, desc: String): AnnotationVisitor { + val (annotation, visitor) = + BinaryJavaAnnotation.createAnnotationAndVisitor(desc, context, signatureParser) + + arguments.add(PlainJavaAnnotationAsAnnotationArgument(name, annotation)) + + return visitor + } + + override fun visitEnum(name: String?, desc: String, value: String) { + addArgument(PlainJavaEnumValueAnnotationArgument(name, desc, value, context)) + } + + override fun visit(name: String?, value: Any?) { + addArgument(convertConstValue(name, value)) + } + + private fun convertConstValue(name: String?, value: Any?): JavaAnnotationArgument? { + return when (value) { + is Byte, is Boolean, is Char, is Short, is Int, is Long, is Float, is Double, is String -> + PlainJavaLiteralAnnotationArgument(name, value) + is Type -> PlainJavaClassObjectAnnotationArgument(name, value, signatureParser, context) + else -> value?.takeIf { it.javaClass.isArray }?.let { array -> + val arguments = (0 until Array.getLength(array)).mapNotNull { index -> + convertConstValue(name = null, value = Array.get(array, index)) + } + + PlainJavaArrayAnnotationArgument(name, arguments) + } + } + } + + override fun visitArray(name: String?): AnnotationVisitor { + val result = mutableListOf() + addArgument(PlainJavaArrayAnnotationArgument(name, result)) + + return BinaryJavaAnnotationVisitor(context, signatureParser, result) + } +} + +abstract class PlainJavaAnnotationArgument(name: String?) : JavaAnnotationArgument { + override val name: Name? = name?.takeIf(Name::isValidIdentifier)?.let(Name::identifier) +} + +class PlainJavaLiteralAnnotationArgument( + name: String?, + override val value: Any? +) : PlainJavaAnnotationArgument(name), JavaLiteralAnnotationArgument + +class PlainJavaClassObjectAnnotationArgument( + name: String?, + private val type: Type, + private val signatureParser: BinaryClassSignatureParser, + private val context: ClassifierResolutionContext +) : PlainJavaAnnotationArgument(name), JavaClassObjectAnnotationArgument { + override fun getReferencedType() = signatureParser.mapAsmType(type, context) +} + +class PlainJavaArrayAnnotationArgument( + name: String?, + private val elements: List +) : PlainJavaAnnotationArgument(name), JavaArrayAnnotationArgument { + override fun getElements(): List = elements +} + +class PlainJavaAnnotationAsAnnotationArgument( + name: String?, + private val annotation: JavaAnnotation +) : PlainJavaAnnotationArgument(name), JavaAnnotationAsAnnotationArgument { + override fun getAnnotation() = annotation +} + +class PlainJavaEnumValueAnnotationArgument( + name: String?, + private val desc: String, + private val entryName: String, + private val context: ClassifierResolutionContext +) : PlainJavaAnnotationArgument(name), JavaEnumValueAnnotationArgument { + override fun resolve(): JavaField? { + val javaClass = context.resolveByInternalName(Type.getType(desc).internalName).classifier as? JavaClass ?: return null + return javaClass.fields.singleOrNull { it.name.asString() == entryName } + } +} + +private fun JavaClass.classId(): ClassId? { + val fqName = fqName ?: return null + if (outerClass == null) return ClassId.topLevel(fqName) + + val outerClassId = outerClass!!.classId() ?: return null + + return ClassId(outerClassId.packageFqName, outerClassId.relativeClassName.child(name), false) +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryClassSignatureParser.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryClassSignatureParser.kt new file mode 100644 index 00000000000..518edabd439 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryClassSignatureParser.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import com.intellij.psi.CommonClassNames +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.load.java.structure.JavaClassifierType +import org.jetbrains.kotlin.load.java.structure.JavaType +import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.org.objectweb.asm.Type +import java.text.CharacterIterator +import java.text.StringCharacterIterator + +/** + * Take a look at com.intellij.psi.impl.compiled.SignatureParsing + */ +class BinaryClassSignatureParser(globalContext: ClassifierResolutionContext) { + companion object { + private val JAVA_LANG_OBJECT = ClassId.topLevel(FqName(CommonClassNames.JAVA_LANG_OBJECT)) + } + + private val JAVA_LANG_OBJECT_CLASSIFIER_TYPE: JavaClassifierType = + PlainJavaClassifierType({ globalContext.resolveClass(JAVA_LANG_OBJECT) }, emptyList()) + + fun parseTypeParametersDeclaration(signature: CharacterIterator, context: ClassifierResolutionContext): List { + if (signature.current() != '<') { + return emptyList() + } + + val typeParameters = ContainerUtil.newArrayList() + signature.next() + while (signature.current() != '>') { + typeParameters.add(parseTypeParameter(signature, context)) + } + signature.next() + return typeParameters + } + + private fun parseTypeParameter(signature: CharacterIterator, context: ClassifierResolutionContext): JavaTypeParameter { + val name = StringBuilder() + while (signature.current() != ':' && signature.current() != CharacterIterator.DONE) { + name.append(signature.current()) + signature.next() + } + if (signature.current() == CharacterIterator.DONE) { + throw ClsFormatException() + } + val parameterName = name.toString() + + // postpone list allocation till a second bound is seen; ignore sole Object bound + var bounds: MutableList? = null + var jlo = false + while (signature.current() == ':') { + signature.next() + val bound = parseClassifierRefSignature(signature, context) ?: continue + if (bounds == null) { + if (JAVA_LANG_OBJECT_CLASSIFIER_TYPE === bound) { + jlo = true + continue + } + bounds = ContainerUtil.newSmartList() + if (jlo) { + bounds.add(JAVA_LANG_OBJECT_CLASSIFIER_TYPE) + } + } + bounds.add(bound) + } + + return BinaryJavaTypeParameter(Name.identifier(parameterName), bounds ?: emptyList()) + } + + fun parseClassifierRefSignature(signature: CharacterIterator, context: ClassifierResolutionContext): JavaClassifierType? { + return when (signature.current()) { + 'L' -> parseParameterizedClassRefSignature(signature, context) + 'T' -> parseTypeVariableRefSignature(signature, context) + else -> null + } + } + + private fun parseTypeVariableRefSignature(signature: CharacterIterator, context: ClassifierResolutionContext): JavaClassifierType? { + val id = StringBuilder() + + signature.next() + while (signature.current() != ';' && signature.current() != '>' && signature.current() != CharacterIterator.DONE) { + id.append(signature.current()) + signature.next() + } + + if (signature.current() == CharacterIterator.DONE) { + throw ClsFormatException() + } + if (signature.current() == ';') { + signature.next() + } + + return PlainJavaClassifierType({ context.resolveTypeParameter(id.toString()) }, emptyList()) + } + + private fun parseParameterizedClassRefSignature( + signature: CharacterIterator, + context: ClassifierResolutionContext + ): JavaClassifierType { + val canonicalName = StringBuilder() + + val argumentGroups = ContainerUtil.newSmartList>() + + signature.next() + while (signature.current() != ';' && signature.current() != CharacterIterator.DONE) { + val c = signature.current() + if (c == '<') { + val group = mutableListOf() + signature.next() + do { + group.add(parseClassOrTypeVariableElement(signature, context)) + } + while (signature.current() != '>') + + argumentGroups.add(group) + } + else if (c != ' ') { + canonicalName.append(c) + } + signature.next() + } + + if (signature.current() == CharacterIterator.DONE) { + throw ClsFormatException() + } + signature.next() + + if (canonicalName.toString() == "java/lang/Object") return JAVA_LANG_OBJECT_CLASSIFIER_TYPE + + return PlainJavaClassifierType({ context.resolveByInternalName(canonicalName.toString()) }, argumentGroups.reversed().flatten()) + } + + private fun parseClassOrTypeVariableElement(signature: CharacterIterator, context: ClassifierResolutionContext): JavaType { + val variance = parseVariance(signature) + if (variance == JavaSignatureVariance.STAR) { + return PlainJavaWildcardType(bound = null, isExtends = true) + } + + val type = parseTypeString(signature, context) + if (variance == JavaSignatureVariance.NO_VARIANCE) return type + + return PlainJavaWildcardType(type, isExtends = variance == JavaSignatureVariance.PLUS) + } + + private enum class JavaSignatureVariance { + PLUS, MINUS, STAR, NO_VARIANCE + } + + private fun parseVariance(signature: CharacterIterator): JavaSignatureVariance { + var advance = true + + val variance = when (signature.current()) { + '+' -> JavaSignatureVariance.PLUS + '-' -> JavaSignatureVariance.MINUS + '*' -> JavaSignatureVariance.STAR + '.', '=' -> JavaSignatureVariance.NO_VARIANCE + else -> { + advance = false + JavaSignatureVariance.NO_VARIANCE + } + } + + if (advance) { + signature.next() + } + + return variance + } + + private fun parseDimensions(signature: CharacterIterator): Int { + var dimensions = 0 + while (signature.current() == '[') { + dimensions++ + signature.next() + } + return dimensions + } + + fun parseTypeString(signature: CharacterIterator, context: ClassifierResolutionContext): JavaType { + val dimensions = parseDimensions(signature) + + val type: JavaType = parseTypeWithoutVarianceAndArray(signature, context) ?: throw ClsFormatException() + return (1..dimensions).fold(type) { result, _ -> PlainJavaArrayType(result) } + } + + fun mapAsmType(type: Type, context: ClassifierResolutionContext) = parseTypeString(StringCharacterIterator(type.descriptor), context) + + private fun parseTypeWithoutVarianceAndArray(signature: CharacterIterator, context: ClassifierResolutionContext) = + when (signature.current()) { + 'L' -> parseParameterizedClassRefSignature(signature, context) + 'T' -> parseTypeVariableRefSignature(signature, context) + + 'B' -> parsePrimitiveType(signature, PrimitiveType.BYTE) + 'C' -> parsePrimitiveType(signature, PrimitiveType.CHAR) + 'D' -> parsePrimitiveType(signature, PrimitiveType.DOUBLE) + 'F' -> parsePrimitiveType(signature, PrimitiveType.FLOAT) + 'I' -> parsePrimitiveType(signature, PrimitiveType.INT) + 'J' -> parsePrimitiveType(signature, PrimitiveType.LONG) + 'Z' -> parsePrimitiveType(signature, PrimitiveType.BOOLEAN) + 'S' -> parsePrimitiveType(signature, PrimitiveType.SHORT) + 'V' -> parsePrimitiveType(signature, null) + else -> null + } + + private fun parsePrimitiveType(signature: CharacterIterator, primitiveType: PrimitiveType?): JavaType { + signature.next() + return PlainJavaPrimitiveType(primitiveType) + } + + class ClsFormatException(message: String? = null, cause: Throwable? = null) : Throwable(message, cause) +} 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 new file mode 100644 index 00000000000..8cbf999ec75 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/BinaryJavaClass.kt @@ -0,0 +1,194 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.containers.ContainerUtil +import gnu.trove.THashMap +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.addIfNotNull +import org.jetbrains.org.objectweb.asm.* +import java.text.CharacterIterator +import java.text.StringCharacterIterator + +class BinaryJavaClass( + val virtualFile: VirtualFile, + override val fqName: FqName, + private val context: ClassifierResolutionContext, + private val signatureParser: BinaryClassSignatureParser, + override var access: Int = 0, + override val outerClass: JavaClass?, + classContent: ByteArray? = null +) : ClassVisitor(ASM_API_VERSION_FOR_CLASS_READING), JavaClass, BinaryJavaModifierListOwner, BinaryJavaAnnotationOwner { + lateinit var myInternalName: String + + override val annotations: MutableCollection = mutableListOf() + override lateinit var typeParameters: List + override lateinit var supertypes: Collection + override val methods = arrayListOf() + override val fields = arrayListOf() + override val constructors = arrayListOf() + + override val annotationsByFqName by buildLazyValueForMap() + + private val innerClassNameToAccess: MutableMap = THashMap() + override val innerClassNames get() = innerClassNameToAccess.keys + + override val name: Name + get() = fqName.shortName() + + override val isInterface get() = isSet(Opcodes.ACC_INTERFACE) + override val isAnnotationType get() = isSet(Opcodes.ACC_ANNOTATION) + override val isEnum get() = isSet(Opcodes.ACC_ENUM) + override val lightClassOriginKind: LightClassOriginKind? get() = null + + override fun visitEnd() { + methods.trimToSize() + fields.trimToSize() + constructors.trimToSize() + } + + init { + ClassReader(classContent ?: virtualFile.contentsToByteArray()).accept( + this, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES + ) + } + + override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor? { + if (access.isSet(Opcodes.ACC_SYNTHETIC) || access.isSet(Opcodes.ACC_BRIDGE) || name == "") return null + + // skip semi-synthetic enum methods + if (isEnum) { + if (name == "values" && desc.startsWith("()")) return null + if (name == "valueOf" && desc.startsWith("(Ljava/lang/String;)")) return null + } + + val (member, visitor) = BinaryJavaMethodBase.create(name, access, desc, signature, this, context.copyForMember(), signatureParser) + + when (member) { + is JavaMethod -> methods.add(member) + is JavaConstructor -> constructors.add(member) + else -> error("Unexpected member: ${member.javaClass}") + } + + return visitor + } + + override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) { + if (access.isSet(Opcodes.ACC_SYNTHETIC)) return + if (innerName == null || outerName == null) return + + if (myInternalName == outerName) { + context.addInnerClass(name, outerName, innerName) + innerClassNameToAccess[context.mapInternalNameToClassId(name).shortClassName] = access + } + } + + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array? + ) { + this.access = this.access or access + this.myInternalName = name + + if (signature != null) { + parseClassSignature(signature) + } + else { + this.typeParameters = emptyList() + this.supertypes = mutableListOf().apply { + addIfNotNull(superName?.convertInternalNameToClassifierType()) + interfaces?.forEach { + addIfNotNull(it.convertInternalNameToClassifierType()) + } + } + } + } + + private fun parseClassSignature(signature: String) { + val iterator = StringCharacterIterator(signature) + this.typeParameters = + signatureParser + .parseTypeParametersDeclaration(iterator, context) + .also(context::addTypeParameters) + + val supertypes = ContainerUtil.newSmartList() + supertypes.addIfNotNull(signatureParser.parseClassifierRefSignature(iterator, context)) + while (iterator.current() != CharacterIterator.DONE) { + supertypes.addIfNotNull(signatureParser.parseClassifierRefSignature(iterator, context)) + } + this.supertypes = supertypes + } + + private fun String.convertInternalNameToClassifierType(): JavaClassifierType = + PlainJavaClassifierType({ context.resolveByInternalName(this) }, emptyList()) + + override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? { + if (access.isSet(Opcodes.ACC_SYNTHETIC)) return null + + val type = signatureParser.parseTypeString(StringCharacterIterator(signature ?: desc), context) + + val processedValue = processValue(value, type) + + return BinaryJavaField(Name.identifier(name), access, this, access.isSet(Opcodes.ACC_ENUM), type, processedValue).run { + fields.add(this) + + object : FieldVisitor(ASM_API_VERSION_FOR_CLASS_READING) { + override fun visitAnnotation(desc: String, visible: Boolean) = + BinaryJavaAnnotation.addAnnotation(this@run.annotations, desc, context, signatureParser) + } + } + } + + /** + * All the int-like values (including Char/Boolean) come in visitor as Integer instances + */ + private fun processValue(value: Any?, fieldType: JavaType): Any? { + if (fieldType !is JavaPrimitiveType || fieldType.type == null || value !is Int) return value + + return when (fieldType.type) { + PrimitiveType.BOOLEAN -> { + when (value) { + 0 -> false + 1 -> true + else -> value + } + } + PrimitiveType.CHAR -> value.toChar() + else -> value + } + } + + override fun visitAnnotation(desc: String, visible: Boolean) = + BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser) + + override fun findInnerClass(name: Name): JavaClass? { + val access = innerClassNameToAccess[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 new file mode 100644 index 00000000000..c1ef24ac605 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/ClassifierResolutionContext.kt @@ -0,0 +1,120 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import com.intellij.util.containers.ContainerUtil +import gnu.trove.THashMap +import org.jetbrains.kotlin.load.java.structure.JavaClass +import org.jetbrains.kotlin.load.java.structure.JavaClassifier +import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +typealias ClassIdToJavaClass = (ClassId) -> JavaClass? + +class ClassifierResolutionContext private constructor( + private val classesByQName: ClassIdToJavaClass, + // Note that this data is fully mutable and its correctness is based on the assumption + // that nobody starts resolving classifier until type parameters and inner classes are initialized. + // Currently it's implemented through laziness in the PlainJavaClassifierType. + private var typeParameters: MutableMap?, + private var innerClasses: MutableMap? +) { + constructor(classesByQName: ClassIdToJavaClass) : this(classesByQName, null, null) + + internal data class Result(val classifier: JavaClassifier?, val qualifiedName: String) + + private class InnerClassInfo(val outerInternalName: String, val simpleName: String) + + internal fun addInnerClass(innerInternalName: String, outerInternalName: String, simpleName: String) { + if (innerClasses == null) { + innerClasses = THashMap() + } + + innerClasses!!.put(innerInternalName, InnerClassInfo(outerInternalName, simpleName)) + } + + internal fun addTypeParameters(newTypeParameters: Collection) { + if (newTypeParameters.isEmpty()) return + if (typeParameters == null) { + typeParameters = THashMap() + } + + newTypeParameters.associateByTo(typeParameters!!) { it.name.identifier } + } + + internal fun resolveClass(classId: ClassId) = Result(classesByQName(classId), classId.asSingleFqName().asString()) + internal fun resolveTypeParameter(name: String) = Result(typeParameters?.get(name), name) + + internal fun copyForMember() = + ClassifierResolutionContext(classesByQName, typeParameters?.let(::THashMap), innerClasses?.let(::THashMap)) + + // See com.intellij.psi.impl.compiled.StubBuildingVisitor.createMapping(byte[]) + internal fun mapInternalNameToClassId(internalName: String): ClassId { + if ('.' in internalName) { + val parts = internalName.split('.') + + val outerClass = mapInternalNameToClassId(parts[0]) + val nestedParts = parts.subList(1, parts.size) + + return nestedParts.fold(outerClass) { classId, part -> + classId.createNestedClassId(Name.identifier(part)) + } + } + + if ('$' in internalName) { + val innerClassInfo = innerClasses?.get(internalName) ?: return mapInternalNameToClassIdNaively(internalName) + if (Name.isValidIdentifier(innerClassInfo.simpleName)) { + val outerClassId = mapInternalNameToClassId(innerClassInfo.outerInternalName) + return outerClassId.createNestedClassId(Name.identifier(innerClassInfo.simpleName)) + } + } + + return createClassIdForTopLevel(internalName) + } + + // 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)) +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Methods.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Methods.kt new file mode 100644 index 00000000000..c0cdff99966 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Methods.kt @@ -0,0 +1,170 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import com.intellij.util.cls.ClsFormatException +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.org.objectweb.asm.MethodVisitor +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type +import java.text.CharacterIterator +import java.text.StringCharacterIterator + +abstract class BinaryJavaMethodBase( + override val access: Int, + override val containingClass: JavaClass, + val valueParameters: List, + val typeParameters: List, + override val name: Name +) : JavaMember, BinaryJavaAnnotationOwner, BinaryJavaModifierListOwner { + override val annotationsByFqName by buildLazyValueForMap() + + override val annotations: Collection = mutableListOf() + + companion object { + private class MethodInfo( + val returnType: JavaType, + val typeParameters: List, + val valueParameterTypes: List + ) + + fun create( + name: String, + access: Int, + desc: String, + signature: String?, + containingClass: JavaClass, + parentContext: ClassifierResolutionContext, + signatureParser: BinaryClassSignatureParser + ): Pair { + val isConstructor = "" == name + val isVarargs = access.isSet(Opcodes.ACC_VARARGS) + + val isInnerClassConstructor = isConstructor && containingClass.outerClass != null && !containingClass.isStatic + val isEnumConstructor = containingClass.isEnum && isConstructor + val info: MethodInfo = + if (signature != null) { + val contextForMethod = parentContext.copyForMember() + parseMethodSignature(signature, signatureParser, contextForMethod).also { + contextForMethod.addTypeParameters(it.typeParameters) + } + } else + parseMethodDescription(desc, parentContext, signatureParser).let { + when { + isEnumConstructor -> + // skip ordinal/name parameters for enum constructors + MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(2)) + isInnerClassConstructor -> + // omit synthetic inner class constructor parameter + MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(1)) + else -> it + } + } + + val parameterTypes = info.valueParameterTypes + val parameterList = ContainerUtil.newSmartList() + val paramCount = parameterTypes.size + for (i in 0..paramCount - 1) { + val type = parameterTypes[i] + val isEllipsisParam = isVarargs && i == paramCount - 1 + + parameterList.add(BinaryJavaValueParameter(null, type, isEllipsisParam)) + } + + val member: BinaryJavaMethodBase = + if (isConstructor) + BinaryJavaConstructor(access, containingClass, parameterList, info.typeParameters) + else + BinaryJavaMethod( + access, containingClass, parameterList, info.typeParameters, Name.identifier(name), info.returnType + ) + + val paramIgnoreCount = when { + isEnumConstructor -> 2 + isInnerClassConstructor -> 1 + else -> 0 + } + + return member to AnnotationsCollectorMethodVisitor(member, parentContext, signatureParser, paramIgnoreCount) + } + + private fun parseMethodDescription( + desc: String, + context: ClassifierResolutionContext, + signatureParser: BinaryClassSignatureParser + ): MethodInfo { + val returnType = signatureParser.mapAsmType(Type.getReturnType(desc), context) + val parameterTypes = Type.getArgumentTypes(desc).map { signatureParser.mapAsmType(it, context) } + + return MethodInfo(returnType, emptyList(), parameterTypes) + } + + private fun parseMethodSignature( + signature: String, + signatureParser: BinaryClassSignatureParser, + context: ClassifierResolutionContext + ): MethodInfo { + val iterator = StringCharacterIterator(signature) + val typeParameters = signatureParser.parseTypeParametersDeclaration(iterator, context) + + if (iterator.current() != '(') throw ClsFormatException() + iterator.next() + val paramTypes: List + if (iterator.current() == ')') { + paramTypes = emptyList() + } + else { + paramTypes = ContainerUtil.newSmartList() + while (iterator.current() != ')' && iterator.current() != CharacterIterator.DONE) { + paramTypes.add(signatureParser.parseTypeString(iterator, context)) + } + if (iterator.current() != ')') throw ClsFormatException() + } + iterator.next() + + val returnType = signatureParser.parseTypeString(iterator, context) + + return MethodInfo(returnType, typeParameters, paramTypes) + } + } +} + +class BinaryJavaMethod( + flags: Int, + containingClass: JavaClass, + valueParameters: List, + typeParameters: List, + name: Name, + override val returnType: JavaType +) : BinaryJavaMethodBase( + flags, containingClass, valueParameters, typeParameters, name +), JavaMethod { + override var hasAnnotationParameterDefaultValue: Boolean = false +} + +class BinaryJavaConstructor( + flags: Int, + containingClass: JavaClass, + valueParameters: List, + typeParameters: List +) : BinaryJavaMethodBase( + flags, containingClass, valueParameters, typeParameters, + SpecialNames.NO_NAME_PROVIDED +), JavaConstructor 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 new file mode 100644 index 00000000000..6e326a8614e --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Other.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.org.objectweb.asm.ClassReader +import org.jetbrains.org.objectweb.asm.ClassVisitor + +class BinaryJavaField( + override val name: Name, + override val access: Int, + override val containingClass: JavaClass, + override val isEnumEntry: Boolean, + override val type: JavaType, + val compiledValue: Any? +) : JavaField, BinaryJavaAnnotationOwner, BinaryJavaModifierListOwner { + override val annotations: MutableCollection = ContainerUtil.newSmartList() + override val annotationsByFqName by buildLazyValueForMap() +} + +class BinaryJavaTypeParameter( + override val name: Name, + override val upperBounds: Collection +) : JavaTypeParameter { + // TODO: support annotations on type parameters + override val annotations get() = emptyList() + override fun findAnnotation(fqName: FqName) = null + + override val isDeprecatedInJavaDoc get() = false +} + +class BinaryJavaValueParameter( + override val name: Name?, + override val type: JavaType, + override val isVararg: Boolean +) : JavaValueParameter, BinaryJavaAnnotationOwner { + override val annotations: MutableCollection = ContainerUtil.newSmartList() + override val annotationsByFqName by buildLazyValueForMap() +} + +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 + } + + override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) { + isNotTopLevelClass = isNotTopLevelClass or (name == internalName) + } + }, + ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES + ) + return isNotTopLevelClass +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Types.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Types.kt new file mode 100644 index 00000000000..609011c2b17 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/Types.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +// They are only used for java class files, but potentially may be used in other cases +// It would be better to call them like JavaSomeTypeImpl, but these names are already occupied by the PSI based types +internal class PlainJavaArrayType(override val componentType: JavaType) : JavaArrayType +internal class PlainJavaWildcardType(override val bound: JavaType?, override val isExtends: Boolean) : JavaWildcardType +internal class PlainJavaPrimitiveType(override val type: PrimitiveType?) : JavaPrimitiveType + +internal class PlainJavaClassifierType( + // calculation of classifier and canonicalText + classifierComputation: () -> ClassifierResolutionContext.Result, + override val typeArguments: List +) : JavaClassifierType { + private val classifierResolverResult by lazy(LazyThreadSafetyMode.NONE, classifierComputation) + + override val classifier get() = classifierResolverResult.classifier + override val isRaw + get() = typeArguments.isEmpty() && + classifierResolverResult.classifier?.safeAs()?.typeParameters?.isNotEmpty() == true + + // TODO: support type annotations + override val annotations get() = emptyList() + override fun findAnnotation(fqName: FqName) = null + + override val isDeprecatedInJavaDoc get() = false + + override val classifierQualifiedName: String + get() = classifierResolverResult.qualifiedName + + // TODO: render arguments for presentable text + override val presentableText: String + get() = classifierQualifiedName +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/commonMixins.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/commonMixins.kt new file mode 100644 index 00000000000..b5e8baa1d7f --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/java/structure/impl/classFiles/commonMixins.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2017 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.load.java.structure.impl.classFiles + +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.load.java.JavaVisibilities +import org.jetbrains.kotlin.load.java.structure.JavaAnnotation +import org.jetbrains.kotlin.load.java.structure.JavaAnnotationOwner +import org.jetbrains.kotlin.load.java.structure.JavaModifierListOwner +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.org.objectweb.asm.Opcodes + +internal val ASM_API_VERSION_FOR_CLASS_READING = Opcodes.ASM5 + +internal interface BinaryJavaAnnotationOwner : JavaAnnotationOwner { + val annotationsByFqName: Map + override fun findAnnotation(fqName: FqName) = annotationsByFqName[fqName] + override val isDeprecatedInJavaDoc: Boolean + get() = false +} + +internal interface BinaryJavaModifierListOwner : JavaModifierListOwner, BinaryJavaAnnotationOwner { + val access: Int + + fun isSet(flag: Int) = access.isSet(flag) + + override val isAbstract get() = isSet(Opcodes.ACC_ABSTRACT) + override val isStatic get() = isSet(Opcodes.ACC_STATIC) + override val isFinal get() = isSet(Opcodes.ACC_FINAL) + override val visibility: Visibility + get() = when { + isSet(Opcodes.ACC_PRIVATE) -> Visibilities.PRIVATE + isSet(Opcodes.ACC_PROTECTED) -> + if (isStatic) JavaVisibilities.PROTECTED_STATIC_VISIBILITY else JavaVisibilities.PROTECTED_AND_PACKAGE + isSet(Opcodes.ACC_PUBLIC) -> Visibilities.PUBLIC + else -> JavaVisibilities.PACKAGE_VISIBILITY + } + + override val isDeprecatedInJavaDoc get() = isSet(Opcodes.ACC_DEPRECATED) +} + +internal fun JavaAnnotationOwner.buildLazyValueForMap() = lazy { + annotations.filter { it.classId != null }.associateBy({ it.classId!!.asSingleFqName() }, { it }) +} + +internal fun Int.isSet(flag: Int) = this and flag != 0 diff --git a/compiler/testData/loadJava/compiledJava/TopLevel$Class.java b/compiler/testData/loadJava/compiledJava/TopLevel$Class.java new file mode 100644 index 00000000000..ded7a5e63df --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/TopLevel$Class.java @@ -0,0 +1,5 @@ +package test; + +public class TopLevel$Class { + public void foo(TopLevel$Class other) {} +} diff --git a/compiler/testData/loadJava/compiledJava/TopLevel$Class.runtime.txt b/compiler/testData/loadJava/compiledJava/TopLevel$Class.runtime.txt new file mode 100644 index 00000000000..8188147ab25 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/TopLevel$Class.runtime.txt @@ -0,0 +1,6 @@ +package test + +public open class `TopLevel$Class` { + public constructor `TopLevel$Class`() + public open fun foo(/*0*/ test.`TopLevel$Class`!): kotlin.Unit +} diff --git a/compiler/testData/loadJava/compiledJava/TopLevel$Class.txt b/compiler/testData/loadJava/compiledJava/TopLevel$Class.txt new file mode 100644 index 00000000000..8d0d3e3dd3e --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/TopLevel$Class.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/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java index 2e638538506..68adfe669fd 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/LoadJavaTestGenerated.java @@ -284,6 +284,12 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledJava(fileName); } + @TestMetadata("TopLevel$Class.java") + public void testTopLevel$Class() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/TopLevel$Class.java"); + doTestCompiledJava(fileName); + } + @TestMetadata("TwoFields.java") public void testTwoFields() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/TwoFields.java"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index edd35559352..07716e364d4 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -3410,6 +3410,12 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD doTest(fileName); } + @TestMetadata("TopLevel$Class.java") + public void testTopLevel$Class() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/TopLevel$Class.java"); + doTest(fileName); + } + @TestMetadata("TwoFields.java") public void testTwoFields() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/loadJava/compiledJava/TwoFields.java");