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:
+148
@@ -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)
|
||||
}
|
||||
}
|
||||
+226
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user