Introduce custom Java model implementation for class-files
It's only used for CLI compiler, and it should improve performance of loading Java descriptors from class-files For IntelliJ, it leads to 10-15% percent speedup of Kotlin Builder Before this change, we were using a Java model based on Java PSI that also read class files, but do it less effectively since it performs some extra work, that we don't need, e.g. eagerly reading all the inner classes
This commit is contained in:
+38
@@ -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<FqName, VirtualFile?> = 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<ClassId, JavaClass?> = 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)
|
||||
}
|
||||
|
||||
|
||||
+203
@@ -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<BinaryJavaMethod>()?.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<JavaAnnotation>,
|
||||
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<JavaAnnotation>?
|
||||
?: return null
|
||||
|
||||
return BinaryJavaAnnotation.addAnnotation(annotations, desc, context, signatureParser)
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryJavaAnnotation private constructor(
|
||||
desc: String,
|
||||
private val context: ClassifierResolutionContext,
|
||||
override val arguments: Collection<JavaAnnotationArgument>
|
||||
) : JavaAnnotation {
|
||||
|
||||
companion object {
|
||||
|
||||
fun createAnnotationAndVisitor(
|
||||
desc: String,
|
||||
context: ClassifierResolutionContext,
|
||||
signatureParser: BinaryClassSignatureParser
|
||||
): Pair<JavaAnnotation, AnnotationVisitor> {
|
||||
val arguments = mutableListOf<JavaAnnotationArgument>()
|
||||
val annotation = BinaryJavaAnnotation(desc, context, arguments)
|
||||
|
||||
return annotation to BinaryJavaAnnotationVisitor(context, signatureParser, arguments)
|
||||
}
|
||||
|
||||
fun addAnnotation(
|
||||
annotations: MutableCollection<JavaAnnotation>,
|
||||
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<JavaClass>()?.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<JavaAnnotationArgument>
|
||||
) : 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<JavaAnnotationArgument>()
|
||||
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<JavaAnnotationArgument>
|
||||
) : PlainJavaAnnotationArgument(name), JavaArrayAnnotationArgument {
|
||||
override fun getElements(): List<JavaAnnotationArgument> = 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)
|
||||
}
|
||||
+232
@@ -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<JavaTypeParameter> {
|
||||
if (signature.current() != '<') {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val typeParameters = ContainerUtil.newArrayList<JavaTypeParameter>()
|
||||
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<JavaClassifierType>? = 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<List<JavaType>>()
|
||||
|
||||
signature.next()
|
||||
while (signature.current() != ';' && signature.current() != CharacterIterator.DONE) {
|
||||
val c = signature.current()
|
||||
if (c == '<') {
|
||||
val group = mutableListOf<JavaType>()
|
||||
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)
|
||||
}
|
||||
+194
@@ -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<JavaAnnotation> = mutableListOf()
|
||||
override lateinit var typeParameters: List<JavaTypeParameter>
|
||||
override lateinit var supertypes: Collection<JavaClassifierType>
|
||||
override val methods = arrayListOf<JavaMethod>()
|
||||
override val fields = arrayListOf<JavaField>()
|
||||
override val constructors = arrayListOf<JavaConstructor>()
|
||||
|
||||
override val annotationsByFqName by buildLazyValueForMap()
|
||||
|
||||
private val innerClassNameToAccess: MutableMap<Name, Int> = 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<out String>?): MethodVisitor? {
|
||||
if (access.isSet(Opcodes.ACC_SYNTHETIC) || access.isSet(Opcodes.ACC_BRIDGE) || name == "<clinit>") 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<out String>?
|
||||
) {
|
||||
this.access = this.access or access
|
||||
this.myInternalName = name
|
||||
|
||||
if (signature != null) {
|
||||
parseClassSignature(signature)
|
||||
}
|
||||
else {
|
||||
this.typeParameters = emptyList()
|
||||
this.supertypes = mutableListOf<JavaClassifierType>().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<JavaClassifierType>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+120
@@ -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<String, JavaTypeParameter>?,
|
||||
private var innerClasses: MutableMap<String, InnerClassInfo>?
|
||||
) {
|
||||
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<JavaTypeParameter>) {
|
||||
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<Int>()
|
||||
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))
|
||||
}
|
||||
+170
@@ -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<JavaValueParameter>,
|
||||
val typeParameters: List<JavaTypeParameter>,
|
||||
override val name: Name
|
||||
) : JavaMember, BinaryJavaAnnotationOwner, BinaryJavaModifierListOwner {
|
||||
override val annotationsByFqName by buildLazyValueForMap()
|
||||
|
||||
override val annotations: Collection<JavaAnnotation> = mutableListOf()
|
||||
|
||||
companion object {
|
||||
private class MethodInfo(
|
||||
val returnType: JavaType,
|
||||
val typeParameters: List<JavaTypeParameter>,
|
||||
val valueParameterTypes: List<JavaType>
|
||||
)
|
||||
|
||||
fun create(
|
||||
name: String,
|
||||
access: Int,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
containingClass: JavaClass,
|
||||
parentContext: ClassifierResolutionContext,
|
||||
signatureParser: BinaryClassSignatureParser
|
||||
): Pair<JavaMember, MethodVisitor> {
|
||||
val isConstructor = "<init>" == 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<JavaValueParameter>()
|
||||
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<JavaType>
|
||||
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<JavaValueParameter>,
|
||||
typeParameters: List<JavaTypeParameter>,
|
||||
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<JavaValueParameter>,
|
||||
typeParameters: List<JavaTypeParameter>
|
||||
) : BinaryJavaMethodBase(
|
||||
flags, containingClass, valueParameters, typeParameters,
|
||||
SpecialNames.NO_NAME_PROVIDED
|
||||
), JavaConstructor
|
||||
+81
@@ -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<JavaAnnotation> = ContainerUtil.newSmartList()
|
||||
override val annotationsByFqName by buildLazyValueForMap()
|
||||
}
|
||||
|
||||
class BinaryJavaTypeParameter(
|
||||
override val name: Name,
|
||||
override val upperBounds: Collection<JavaClassifierType>
|
||||
) : JavaTypeParameter {
|
||||
// TODO: support annotations on type parameters
|
||||
override val annotations get() = emptyList<JavaAnnotation>()
|
||||
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<JavaAnnotation> = 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<out String>?
|
||||
) {
|
||||
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
|
||||
}
|
||||
+54
@@ -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<JavaType>
|
||||
) : JavaClassifierType {
|
||||
private val classifierResolverResult by lazy(LazyThreadSafetyMode.NONE, classifierComputation)
|
||||
|
||||
override val classifier get() = classifierResolverResult.classifier
|
||||
override val isRaw
|
||||
get() = typeArguments.isEmpty() &&
|
||||
classifierResolverResult.classifier?.safeAs<JavaClass>()?.typeParameters?.isNotEmpty() == true
|
||||
|
||||
// TODO: support type annotations
|
||||
override val annotations get() = emptyList<JavaAnnotation>()
|
||||
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
|
||||
}
|
||||
+61
@@ -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<FqName, JavaAnnotation>
|
||||
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
|
||||
@@ -0,0 +1,5 @@
|
||||
package test;
|
||||
|
||||
public class TopLevel$Class {
|
||||
public void foo(TopLevel$Class other) {}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user