Support loading Java records
^KT-43677 In Progress
This commit is contained in:
@@ -85,11 +85,13 @@ interface JavaClass : JavaClassifier, JavaTypeParameterListOwner, JavaModifierLi
|
||||
val isInterface: Boolean
|
||||
val isAnnotationType: Boolean
|
||||
val isEnum: Boolean
|
||||
val isRecord: Boolean
|
||||
val lightClassOriginKind: LightClassOriginKind?
|
||||
|
||||
val methods: Collection<JavaMethod>
|
||||
val fields: Collection<JavaField>
|
||||
val constructors: Collection<JavaConstructor>
|
||||
val recordComponents: Collection<JavaRecordComponent>
|
||||
fun hasDefaultConstructor(): Boolean
|
||||
}
|
||||
|
||||
@@ -133,6 +135,11 @@ interface JavaValueParameter : JavaAnnotationOwner {
|
||||
val isVararg: Boolean
|
||||
}
|
||||
|
||||
interface JavaRecordComponent : JavaMember {
|
||||
val type: JavaType
|
||||
val isVararg: Boolean
|
||||
}
|
||||
|
||||
interface JavaTypeParameter : JavaClassifier {
|
||||
val upperBounds: Collection<JavaClassifierType>
|
||||
}
|
||||
|
||||
+4
-6
@@ -22,10 +22,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass;
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaElement;
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaField;
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
|
||||
import org.jetbrains.kotlin.load.java.structure.*;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
|
||||
public interface JavaResolverCache {
|
||||
@@ -37,7 +34,7 @@ public interface JavaResolverCache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordMethod(@NotNull JavaMethod method, @NotNull SimpleFunctionDescriptor descriptor) {
|
||||
public void recordMethod(@NotNull JavaMember member, @NotNull SimpleFunctionDescriptor descriptor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,7 +53,8 @@ public interface JavaResolverCache {
|
||||
@Nullable
|
||||
ClassDescriptor getClassResolvedFromSource(@NotNull FqName fqName);
|
||||
|
||||
void recordMethod(@NotNull JavaMethod method, @NotNull SimpleFunctionDescriptor descriptor);
|
||||
// It's a JavaMethod or JavaRecordComponent
|
||||
void recordMethod(@NotNull JavaMember member, @NotNull SimpleFunctionDescriptor descriptor);
|
||||
|
||||
void recordConstructor(@NotNull JavaElement element, @NotNull ConstructorDescriptor descriptor);
|
||||
|
||||
|
||||
+1
@@ -19,4 +19,5 @@ package org.jetbrains.kotlin.load.java.descriptors;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
|
||||
public interface JavaClassDescriptor extends ClassDescriptor {
|
||||
boolean isRecord();
|
||||
}
|
||||
|
||||
+13
-4
@@ -60,6 +60,7 @@ public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implement
|
||||
}
|
||||
|
||||
private ParameterNamesStatus parameterNamesStatus = null;
|
||||
private final boolean isForRecordComponent;
|
||||
|
||||
protected JavaMethodDescriptor(
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@@ -67,9 +68,11 @@ public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implement
|
||||
@NotNull Annotations annotations,
|
||||
@NotNull Name name,
|
||||
@NotNull Kind kind,
|
||||
@NotNull SourceElement source
|
||||
@NotNull SourceElement source,
|
||||
boolean isForRecordComponent
|
||||
) {
|
||||
super(containingDeclaration, original, annotations, name, kind, source);
|
||||
this.isForRecordComponent = isForRecordComponent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -77,9 +80,10 @@ public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implement
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@NotNull Annotations annotations,
|
||||
@NotNull Name name,
|
||||
@NotNull SourceElement source
|
||||
@NotNull SourceElement source,
|
||||
boolean isForRecordComponent
|
||||
) {
|
||||
return new JavaMethodDescriptor(containingDeclaration, null, annotations, name, Kind.DECLARATION, source);
|
||||
return new JavaMethodDescriptor(containingDeclaration, null, annotations, name, Kind.DECLARATION, source, isForRecordComponent);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -118,6 +122,10 @@ public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implement
|
||||
this.parameterNamesStatus = ParameterNamesStatus.get(hasStableParameterNames, hasSynthesizedParameterNames);
|
||||
}
|
||||
|
||||
public boolean isForRecordComponent() {
|
||||
return isForRecordComponent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected JavaMethodDescriptor createSubstitutedCopy(
|
||||
@@ -134,7 +142,8 @@ public class JavaMethodDescriptor extends SimpleFunctionDescriptorImpl implement
|
||||
annotations,
|
||||
newName != null ? newName : getName(),
|
||||
kind,
|
||||
source
|
||||
source,
|
||||
isForRecordComponent
|
||||
);
|
||||
result.setParameterNamesStatus(hasStableParameterNames(), hasSynthesizedParameterNames());
|
||||
return result;
|
||||
|
||||
+10
@@ -26,12 +26,18 @@ interface DeclaredMemberIndex {
|
||||
fun findFieldByName(name: Name): JavaField?
|
||||
fun getFieldNames(): Set<Name>
|
||||
|
||||
fun getRecordComponentNames(): Set<Name>
|
||||
fun findRecordComponentByName(name: Name): JavaRecordComponent?
|
||||
|
||||
object Empty : DeclaredMemberIndex {
|
||||
override fun findMethodsByName(name: Name) = listOf<JavaMethod>()
|
||||
override fun getMethodNames() = emptySet<Name>()
|
||||
|
||||
override fun findFieldByName(name: Name): JavaField? = null
|
||||
override fun getFieldNames() = emptySet<Name>()
|
||||
|
||||
override fun getRecordComponentNames(): Set<Name> = emptySet()
|
||||
override fun findRecordComponentByName(name: Name): JavaRecordComponent? = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +51,15 @@ open class ClassDeclaredMemberIndex(
|
||||
|
||||
private val methods = jClass.methods.asSequence().filter(methodFilter).groupBy { m -> m.name }
|
||||
private val fields = jClass.fields.asSequence().filter(memberFilter).associateBy { m -> m.name }
|
||||
private val components = jClass.recordComponents.filter(memberFilter).associateBy { it.name }
|
||||
|
||||
override fun findMethodsByName(name: Name): Collection<JavaMethod> = methods[name] ?: listOf()
|
||||
override fun getMethodNames(): Set<Name> = jClass.methods.asSequence().filter(methodFilter).mapTo(mutableSetOf(), JavaMethod::name)
|
||||
|
||||
override fun findFieldByName(name: Name): JavaField? = fields[name]
|
||||
override fun getFieldNames(): Set<Name> = jClass.fields.asSequence().filter(memberFilter).mapTo(mutableSetOf(), JavaField::name)
|
||||
|
||||
override fun getRecordComponentNames(): Set<Name> = components.keys
|
||||
override fun findRecordComponentByName(name: Name): JavaRecordComponent? = components[name]
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -84,6 +84,8 @@ class LazyJavaClassDescriptor(
|
||||
override fun getKind() = kind
|
||||
override fun getModality() = modality
|
||||
|
||||
override fun isRecord(): Boolean = jClass.isRecord
|
||||
|
||||
// To workaround a problem with Scala compatibility (KT-9700),
|
||||
// we consider private visibility of a Java top level class as package private
|
||||
// Shortly: Scala plugin introduces special kind of "private in package" classes
|
||||
|
||||
+94
-5
@@ -39,10 +39,7 @@ import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||
import org.jetbrains.kotlin.load.java.lazy.childForMethod
|
||||
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaConstructor
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.kotlin.computeJvmDescriptor
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -79,23 +76,79 @@ class LazyJavaClassMemberScope(
|
||||
it.memberScope.getFunctionNames()
|
||||
}.apply {
|
||||
addAll(declaredMemberIndex().getMethodNames())
|
||||
addAll(declaredMemberIndex().getRecordComponentNames())
|
||||
addAll(computeClassNames(kindFilter, nameFilter))
|
||||
}
|
||||
|
||||
internal val constructors = c.storageManager.createLazyValue {
|
||||
val constructors = jClass.constructors
|
||||
val result = ArrayList<JavaClassConstructorDescriptor>(constructors.size)
|
||||
val result = ArrayList<ClassConstructorDescriptor>(constructors.size)
|
||||
for (constructor in constructors) {
|
||||
val descriptor = resolveConstructor(constructor)
|
||||
result.add(descriptor)
|
||||
}
|
||||
|
||||
if (jClass.isRecord) {
|
||||
val defaultConstructor = createDefaultRecordConstructor()
|
||||
val jvmDescriptor = defaultConstructor.computeJvmDescriptor(withReturnType = false)
|
||||
|
||||
if (result.none { it.computeJvmDescriptor(withReturnType = false) == jvmDescriptor }) {
|
||||
result.add(defaultConstructor)
|
||||
c.components.javaResolverCache.recordConstructor(jClass, defaultConstructor)
|
||||
}
|
||||
}
|
||||
|
||||
c.components.signatureEnhancement.enhanceSignatures(
|
||||
c,
|
||||
result.ifEmpty { listOfNotNull(createDefaultConstructor()) }
|
||||
).toList()
|
||||
}
|
||||
|
||||
private fun createDefaultRecordConstructor(): ClassConstructorDescriptor {
|
||||
val classDescriptor = ownerDescriptor
|
||||
val constructorDescriptor = JavaClassConstructorDescriptor.createJavaConstructor(
|
||||
classDescriptor, Annotations.EMPTY, /* isPrimary = */ true, c.components.sourceElementFactory.source(jClass)
|
||||
)
|
||||
val valueParameters = createRecordConstructorParameters(constructorDescriptor)
|
||||
constructorDescriptor.setHasSynthesizedParameterNames(false)
|
||||
|
||||
constructorDescriptor.initialize(valueParameters, getConstructorVisibility(classDescriptor))
|
||||
constructorDescriptor.setHasStableParameterNames(false)
|
||||
constructorDescriptor.returnType = classDescriptor.defaultType
|
||||
return constructorDescriptor
|
||||
}
|
||||
|
||||
private fun createRecordConstructorParameters(constructor: ClassConstructorDescriptorImpl): List<ValueParameterDescriptor> {
|
||||
val components = jClass.recordComponents
|
||||
val result = ArrayList<ValueParameterDescriptor>(components.size)
|
||||
|
||||
val attr = TypeUsage.COMMON.toAttributes(isForAnnotationParameter = false)
|
||||
|
||||
for ((index, component) in components.withIndex()) {
|
||||
val parameterType = c.typeResolver.transformJavaType(component.type, attr)
|
||||
val varargElementType =
|
||||
if (component.isVararg) c.components.module.builtIns.getArrayElementType(parameterType) else null
|
||||
|
||||
result.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
constructor,
|
||||
null,
|
||||
index,
|
||||
Annotations.EMPTY,
|
||||
component.name,
|
||||
parameterType,
|
||||
/* deeclaresDefaultValue = */false,
|
||||
/* isCrossinline = */ false,
|
||||
/* isNoinline = */ false,
|
||||
varargElementType,
|
||||
c.components.sourceElementFactory.source(component)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun JavaMethodDescriptor.isVisibleAsFunction(): Boolean {
|
||||
if (jClass.isAnnotationType) return false
|
||||
return isVisibleAsFunctionInCurrentClass(this)
|
||||
@@ -439,6 +492,42 @@ class LazyJavaClassMemberScope(
|
||||
}
|
||||
}
|
||||
|
||||
override fun computeImplicitlyDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
|
||||
if (jClass.isRecord && declaredMemberIndex().findRecordComponentByName(name) != null && result.none { it.valueParameters.isEmpty() }) {
|
||||
result.add(resolveRecordComponentToFunctionDescriptor(declaredMemberIndex().findRecordComponentByName(name)!!))
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveRecordComponentToFunctionDescriptor(recordComponent: JavaRecordComponent): JavaMethodDescriptor {
|
||||
val annotations = c.resolveAnnotations(recordComponent)
|
||||
val functionDescriptorImpl = JavaMethodDescriptor.createJavaMethod(
|
||||
ownerDescriptor, annotations, recordComponent.name, c.components.sourceElementFactory.source(recordComponent), true
|
||||
)
|
||||
|
||||
val returnTypeAttrs = TypeUsage.COMMON.toAttributes(isForAnnotationParameter = false)
|
||||
val returnType = c.typeResolver.transformJavaType(recordComponent.type, returnTypeAttrs)
|
||||
|
||||
functionDescriptorImpl.initialize(
|
||||
null,
|
||||
getDispatchReceiverParameter(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
returnType,
|
||||
// Those functions are generated as open in bytecode
|
||||
// Actually, it should not be important because the class is final anyway, but leaving them open is convenient for consistency
|
||||
Modality.convertFromFlags(abstract = false, open = true),
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
null,
|
||||
)
|
||||
|
||||
functionDescriptorImpl.setParameterNamesStatus(false, false)
|
||||
|
||||
c.components.javaResolverCache.recordMethod(recordComponent, functionDescriptorImpl)
|
||||
|
||||
return functionDescriptorImpl
|
||||
}
|
||||
|
||||
|
||||
override fun computeNonDeclaredProperties(name: Name, result: MutableCollection<PropertyDescriptor>) {
|
||||
if (jClass.isAnnotationType) {
|
||||
computeAnnotationProperties(name, result)
|
||||
|
||||
+8
-1
@@ -82,6 +82,10 @@ abstract class LazyJavaScope(
|
||||
// Fake overrides, values()/valueOf(), etc.
|
||||
protected abstract fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name)
|
||||
|
||||
// It has a similar semantics to computeNonDeclaredFunctions, but it's being called just once per class
|
||||
// While computeNonDeclaredFunctions is being called once per scope instance (once per KotlinTypeRefiner)
|
||||
protected open fun computeImplicitlyDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {}
|
||||
|
||||
protected abstract fun getDispatchReceiverParameter(): ReceiverParameterDescriptor?
|
||||
|
||||
private val declaredFunctions: MemoizedFunctionToNotNull<Name, Collection<SimpleFunctionDescriptor>> =
|
||||
@@ -98,6 +102,8 @@ abstract class LazyJavaScope(
|
||||
result.add(descriptor)
|
||||
}
|
||||
|
||||
computeImplicitlyDeclaredFunctions(result, name)
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@@ -154,7 +160,8 @@ abstract class LazyJavaScope(
|
||||
protected fun resolveMethodToFunctionDescriptor(method: JavaMethod): JavaMethodDescriptor {
|
||||
val annotations = c.resolveAnnotations(method)
|
||||
val functionDescriptorImpl = JavaMethodDescriptor.createJavaMethod(
|
||||
ownerDescriptor, annotations, method.name, c.components.sourceElementFactory.source(method)
|
||||
ownerDescriptor, annotations, method.name, c.components.sourceElementFactory.source(method),
|
||||
declaredMemberIndex().findRecordComponentByName(method.name) != null && method.valueParameters.isEmpty()
|
||||
)
|
||||
|
||||
val c = c.childForMethod(functionDescriptorImpl, method)
|
||||
|
||||
+8
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.descriptors.runtime.structure
|
||||
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaRecordComponent
|
||||
import org.jetbrains.kotlin.load.java.structure.LightClassOriginKind
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -96,6 +97,9 @@ class ReflectJavaClass(
|
||||
.map(::ReflectJavaConstructor)
|
||||
.toList()
|
||||
|
||||
override val recordComponents: Collection<JavaRecordComponent>
|
||||
get() = emptyList()
|
||||
|
||||
override fun hasDefaultConstructor() = false // any default constructor is returned by constructors
|
||||
|
||||
override val lightClassOriginKind: LightClassOriginKind?
|
||||
@@ -114,6 +118,10 @@ class ReflectJavaClass(
|
||||
override val isEnum: Boolean
|
||||
get() = klass.isEnum
|
||||
|
||||
// TODO: Support reflect
|
||||
override val isRecord: Boolean
|
||||
get() = false
|
||||
|
||||
override fun equals(other: Any?) = other is ReflectJavaClass && klass == other.klass
|
||||
|
||||
override fun hashCode() = klass.hashCode()
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ abstract class AbstractJvmRuntimeDescriptorLoaderTest : TestCaseWithTmpdir() {
|
||||
},
|
||||
""
|
||||
)
|
||||
LoadDescriptorUtil.compileJavaWithAnnotationsJar(sources, tmpdir)
|
||||
LoadDescriptorUtil.compileJavaWithAnnotationsJar(sources, tmpdir, emptyList(), null)
|
||||
}
|
||||
fileName.endsWith(".kt") -> {
|
||||
val environment = KotlinTestUtils.createEnvironmentWithJdkAndNullabilityAnnotationsFromIdea(
|
||||
|
||||
Reference in New Issue
Block a user