Add 201 bunch files for JavaClass implementations

In 201, there's an old ASM version and PSI doesn't have record-related API
This commit is contained in:
Denis.Zharkov
2020-12-08 18:27:41 +03:00
parent 5a006a3690
commit 2d8a8d252b
12 changed files with 4298 additions and 0 deletions
@@ -0,0 +1,148 @@
/*
* 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
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiTypeParameter
import com.intellij.psi.search.SearchScope
import org.jetbrains.kotlin.asJava.KtLightClassMarker
import org.jetbrains.kotlin.asJava.isSyntheticValuesOrValueOfMethod
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtPsiUtil
class JavaClassImpl(psiClass: PsiClass) : JavaClassifierImpl<PsiClass>(psiClass), VirtualFileBoundJavaClass, JavaAnnotationOwnerImpl, JavaModifierListOwnerImpl {
init {
assert(psiClass !is PsiTypeParameter) { "PsiTypeParameter should be wrapped in JavaTypeParameter, not JavaClass: use JavaClassifier.create()" }
}
override val innerClassNames: Collection<Name>
get() = psi.innerClasses.mapNotNull { it.name?.takeIf(Name::isValidIdentifier)?.let(Name::identifier) }
override fun findInnerClass(name: Name): JavaClass? {
return psi.findInnerClassByName(name.asString(), false)?.let(::JavaClassImpl)
}
override val fqName: FqName?
get() {
val qualifiedName = psi.qualifiedName
return if (qualifiedName == null) null else FqName(qualifiedName)
}
override val name: Name
get() = KtPsiUtil.safeName(psi.name)
override val isInterface: Boolean
get() = psi.isInterface
override val isAnnotationType: Boolean
get() = psi.isAnnotationType
override val isEnum: Boolean
get() = psi.isEnum
override val isRecord: Boolean
get() = false
override val outerClass: JavaClassImpl?
get() {
val outer = psi.containingClass
return if (outer == null) null else JavaClassImpl(outer)
}
override val typeParameters: List<JavaTypeParameter>
get() = typeParameters(psi.typeParameters)
override val supertypes: Collection<JavaClassifierType>
get() = classifierTypes(psi.superTypes)
override val methods: Collection<JavaMethod>
get() {
assertNotLightClass()
// We apply distinct here because PsiClass#getMethods() can return duplicate PSI methods, for example in Lombok (see KT-11778)
// Return type seems to be null for example for the 'clone' Groovy method generated by @AutoClone (see EA-73795)
return methods(
psi.methods.filter { method ->
!method.isConstructor && method.returnType != null && !(isEnum && isSyntheticValuesOrValueOfMethod(method))
}
).distinct()
}
override val fields: Collection<JavaField>
get() {
assertNotLightClass()
return fields(psi.fields.filter {
// ex. Android plugin generates LightFields for resources started from '.' (.DS_Store file etc)
Name.isValidIdentifier(it.name)
})
}
override val constructors: Collection<JavaConstructor>
get() {
assertNotLightClass()
// See for example org.jetbrains.plugins.scala.lang.psi.light.ScFunctionWrapper,
// which is present in getConstructors(), but its isConstructor() returns false
return constructors(psi.constructors.filter { method -> method.isConstructor })
}
override val recordComponents: Collection<JavaRecordComponent>
get() {
assertNotLightClass()
return emptyList<JavaRecordComponent>()
}
override fun hasDefaultConstructor() = !isInterface && constructors.isEmpty()
override val isAbstract: Boolean
get() = JavaElementUtil.isAbstract(this)
override val isStatic: Boolean
get() = JavaElementUtil.isStatic(this)
override val isFinal: Boolean
get() = JavaElementUtil.isFinal(this)
override val visibility: Visibility
get() = JavaElementUtil.getVisibility(this)
override val lightClassOriginKind: LightClassOriginKind?
get() = (psi as? KtLightClassMarker)?.originKind
override val virtualFile: VirtualFile?
get() = psi.containingFile?.virtualFile
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = psi.containingFile.virtualFile in scope
override fun getAnnotationOwnerPsi() = psi.modifierList
private fun assertNotLightClass() {
val psiClass = psi
if (psiClass !is KtLightClassMarker) return
val message = "Querying members of JavaClass created for $psiClass of type ${psiClass::class.java} defined in file ${psiClass.containingFile?.virtualFile?.canonicalPath}"
LOGGER.error(message)
}
companion object {
private val LOGGER = Logger.getInstance(JavaClassImpl::class.java)
}
}
@@ -0,0 +1,226 @@
/*
* 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.psi.search.SearchScope
import gnu.trove.THashMap
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.org.objectweb.asm.*
import java.text.CharacterIterator
import java.text.StringCharacterIterator
class BinaryJavaClass(
override val virtualFile: VirtualFile,
override val fqName: FqName,
internal 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), VirtualFileBoundJavaClass, BinaryJavaModifierListOwner, MapBasedJavaAnnotationOwner {
private lateinit var myInternalName: String
override val annotations: MutableCollection<JavaAnnotation> = SmartList()
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 recordComponents = arrayListOf<JavaRecordComponent>()
override fun hasDefaultConstructor() = false // never: all constructors explicit in bytecode
override val annotationsByFqName by buildLazyValueForMap()
// Short name of a nested class of this class -> access flags as seen in the InnerClasses attribute value.
// Note that it doesn't include classes mentioned in other InnerClasses attribute values (those which are not nested in this class).
private val ownInnerClassNameToAccess: MutableMap<Name, Int> = THashMap()
override val innerClassNames get() = ownInnerClassNameToAccess.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 isRecord get() = false
override val lightClassOriginKind: LightClassOriginKind? get() = null
override fun isFromSourceCodeInScope(scope: SearchScope): Boolean = false
override fun visitEnd() {
methods.trimToSize()
fields.trimToSize()
constructors.trimToSize()
}
init {
try {
ClassReader(classContent ?: virtualFile.contentsToByteArray()).accept(
this,
ClassReader.SKIP_CODE or ClassReader.SKIP_FRAMES
)
} catch (e: Throwable) {
throw IllegalStateException("Could not read class: $virtualFile", e)
}
}
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
// Do not read InnerClasses attribute values where full name != outer + $ + inner; treat those classes as top level instead.
// This is possible for example for Groovy-generated $Trait$FieldHelper classes.
if (name == "$outerName$$innerName") {
context.addInnerClass(name, outerName, innerName)
if (myInternalName == outerName) {
ownInnerClassNameToAccess[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 = SmartList<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)
override fun visitTypeAnnotation(typeRef: Int, typePath: TypePath?, desc: String, visible: Boolean) =
if (typePath == null)
BinaryJavaAnnotation.addTypeAnnotation(type, desc, context, signatureParser)
else
null
}
}
}
/**
* 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? = findInnerClass(name, classFileContent = null)
fun findInnerClass(name: Name, classFileContent: ByteArray?): JavaClass? {
val access = ownInnerClassNameToAccess[name] ?: return null
return virtualFile.parent.findChild("${virtualFile.nameWithoutExtension}$$name.class")?.let {
BinaryJavaClass(
it, fqName.child(name), context.copyForMember(), signatureParser, access, this,
classFileContent
)
}
}
}