Support loading Java records
^KT-43677 In Progress
This commit is contained in:
+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)
|
||||
|
||||
Reference in New Issue
Block a user