FIR: load Java annotation defaults after binding the class symbol
as they can refer to the class itself.
This commit is contained in:
@@ -174,8 +174,13 @@ class JavaSymbolProvider(
|
|||||||
annotations.any { it.classId?.asSingleFqName() == JvmAnnotationNames.METADATA_FQ_NAME }
|
annotations.any { it.classId?.asSingleFqName() == JvmAnnotationNames.METADATA_FQ_NAME }
|
||||||
|
|
||||||
private class ValueParametersForAnnotationConstructor {
|
private class ValueParametersForAnnotationConstructor {
|
||||||
val valueParameters: MutableList<FirJavaValueParameter> = mutableListOf()
|
val valueParameters: MutableMap<JavaMethod, FirJavaValueParameter> = linkedMapOf()
|
||||||
var valueParameterForValue: FirJavaValueParameter? = null
|
var valueParameterForValue: Pair<JavaMethod, FirJavaValueParameter>? = null
|
||||||
|
|
||||||
|
inline fun forEach(block: (JavaMethod, FirJavaValueParameter) -> Unit) {
|
||||||
|
valueParameterForValue?.let { (javaMethod, firJavaValueParameter) -> block(javaMethod, firJavaValueParameter) }
|
||||||
|
valueParameters.forEach { (javaMethod, firJavaValueParameter) -> block(javaMethod, firJavaValueParameter) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertJavaClassToFir(classSymbol: FirRegularClassSymbol, javaClass: JavaClass): FirJavaClass {
|
private fun convertJavaClassToFir(classSymbol: FirRegularClassSymbol, javaClass: JavaClass): FirJavaClass {
|
||||||
@@ -220,107 +225,127 @@ class JavaSymbolProvider(
|
|||||||
parentClassSymbol: FirRegularClassSymbol?,
|
parentClassSymbol: FirRegularClassSymbol?,
|
||||||
classId: ClassId,
|
classId: ClassId,
|
||||||
javaTypeParameterStack: JavaTypeParameterStack,
|
javaTypeParameterStack: JavaTypeParameterStack,
|
||||||
): FirJavaClass = buildJavaClass {
|
): FirJavaClass {
|
||||||
source = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
|
|
||||||
session = this@JavaSymbolProvider.session
|
|
||||||
symbol = classSymbol
|
|
||||||
name = javaClass.name
|
|
||||||
visibility = javaClass.visibility
|
|
||||||
modality = javaClass.modality
|
|
||||||
classKind = javaClass.classKind
|
|
||||||
this.isTopLevel = outerClassId == null
|
|
||||||
isStatic = javaClass.isStatic
|
|
||||||
this.javaTypeParameterStack = javaTypeParameterStack
|
|
||||||
existingNestedClassifierNames += javaClass.innerClassNames
|
|
||||||
scopeProvider = this@JavaSymbolProvider.scopeProvider
|
|
||||||
val classTypeParameters = javaClass.typeParameters.convertTypeParameters(javaTypeParameterStack)
|
|
||||||
typeParameters += classTypeParameters
|
|
||||||
if (!isStatic && parentClassSymbol != null) {
|
|
||||||
typeParameters += parentClassSymbol.fir.typeParameters.map {
|
|
||||||
buildOuterClassTypeParameterRef { symbol = it.symbol }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val dispatchReceiver = classId.defaultType(typeParameters.map { it.symbol })
|
|
||||||
|
|
||||||
status = FirResolvedDeclarationStatusImpl(
|
|
||||||
javaClass.visibility,
|
|
||||||
javaClass.modality
|
|
||||||
).apply {
|
|
||||||
this.isInner = !isTopLevel && !this@buildJavaClass.isStatic
|
|
||||||
isCompanion = false
|
|
||||||
isData = false
|
|
||||||
isInline = false
|
|
||||||
isFun = classKind == ClassKind.INTERFACE
|
|
||||||
}
|
|
||||||
// TODO: may be we can process fields & methods later.
|
|
||||||
// However, they should be built up to override resolve stage
|
|
||||||
for (javaField in javaClass.fields) {
|
|
||||||
declarations += convertJavaFieldToFir(javaField, classId, javaTypeParameterStack, dispatchReceiver)
|
|
||||||
}
|
|
||||||
val valueParametersForAnnotationConstructor = ValueParametersForAnnotationConstructor()
|
val valueParametersForAnnotationConstructor = ValueParametersForAnnotationConstructor()
|
||||||
val classIsAnnotation = classKind == ClassKind.ANNOTATION_CLASS
|
val classIsAnnotation = javaClass.classKind == ClassKind.ANNOTATION_CLASS
|
||||||
|
return buildJavaClass {
|
||||||
|
source = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
|
||||||
|
session = this@JavaSymbolProvider.session
|
||||||
|
symbol = classSymbol
|
||||||
|
name = javaClass.name
|
||||||
|
visibility = javaClass.visibility
|
||||||
|
modality = javaClass.modality
|
||||||
|
classKind = javaClass.classKind
|
||||||
|
this.isTopLevel = outerClassId == null
|
||||||
|
isStatic = javaClass.isStatic
|
||||||
|
this.javaTypeParameterStack = javaTypeParameterStack
|
||||||
|
existingNestedClassifierNames += javaClass.innerClassNames
|
||||||
|
scopeProvider = this@JavaSymbolProvider.scopeProvider
|
||||||
|
val classTypeParameters = javaClass.typeParameters.convertTypeParameters(javaTypeParameterStack)
|
||||||
|
typeParameters += classTypeParameters
|
||||||
|
if (!isStatic && parentClassSymbol != null) {
|
||||||
|
typeParameters += parentClassSymbol.fir.typeParameters.map {
|
||||||
|
buildOuterClassTypeParameterRef { symbol = it.symbol }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (javaMethod in javaClass.methods) {
|
val dispatchReceiver = classId.defaultType(typeParameters.map { it.symbol })
|
||||||
if (javaMethod.isObjectMethodInInterface()) continue
|
|
||||||
declarations += convertJavaMethodToFir(
|
|
||||||
javaMethod,
|
|
||||||
classId,
|
|
||||||
javaTypeParameterStack,
|
|
||||||
classIsAnnotation,
|
|
||||||
valueParametersForAnnotationConstructor,
|
|
||||||
dispatchReceiver
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val javaClassDeclaredConstructors = javaClass.constructors
|
|
||||||
val constructorId = CallableId(classId.packageFqName, classId.relativeClassName, classId.shortClassName)
|
|
||||||
|
|
||||||
if (javaClassDeclaredConstructors.isEmpty()
|
status = FirResolvedDeclarationStatusImpl(
|
||||||
&& javaClass.classKind == ClassKind.CLASS
|
javaClass.visibility,
|
||||||
&& javaClass.hasDefaultConstructor()
|
javaClass.modality
|
||||||
) {
|
).apply {
|
||||||
declarations += convertJavaConstructorToFir(
|
this.isInner = !isTopLevel && !this@buildJavaClass.isStatic
|
||||||
javaConstructor = null,
|
isCompanion = false
|
||||||
constructorId,
|
isData = false
|
||||||
javaClass,
|
isInline = false
|
||||||
ownerClassBuilder = this,
|
isFun = classKind == ClassKind.INTERFACE
|
||||||
classTypeParameters,
|
}
|
||||||
javaTypeParameterStack
|
// TODO: may be we can process fields & methods later.
|
||||||
)
|
// However, they should be built up to override resolve stage
|
||||||
}
|
for (javaField in javaClass.fields) {
|
||||||
for (javaConstructor in javaClassDeclaredConstructors) {
|
declarations += convertJavaFieldToFir(javaField, classId, javaTypeParameterStack, dispatchReceiver)
|
||||||
declarations += convertJavaConstructorToFir(
|
}
|
||||||
javaConstructor,
|
|
||||||
constructorId,
|
|
||||||
javaClass,
|
|
||||||
ownerClassBuilder = this,
|
|
||||||
classTypeParameters,
|
|
||||||
javaTypeParameterStack,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (classKind == ClassKind.ENUM_CLASS) {
|
for (javaMethod in javaClass.methods) {
|
||||||
generateValuesFunction(
|
if (javaMethod.isObjectMethodInInterface()) continue
|
||||||
session,
|
val firJavaMethod = convertJavaMethodToFir(
|
||||||
classId.packageFqName,
|
javaMethod,
|
||||||
classId.relativeClassName
|
classId,
|
||||||
)
|
javaTypeParameterStack,
|
||||||
generateValueOfFunction(session, classId.packageFqName, classId.relativeClassName)
|
dispatchReceiver
|
||||||
}
|
|
||||||
if (classIsAnnotation) {
|
|
||||||
declarations +=
|
|
||||||
buildConstructorForAnnotationClass(
|
|
||||||
classSource = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement(FirFakeSourceElementKind.ImplicitConstructor) as? FirFakeSourceElement,
|
|
||||||
constructorId = constructorId,
|
|
||||||
ownerClassBuilder = this,
|
|
||||||
valueParametersForAnnotationConstructor = valueParametersForAnnotationConstructor
|
|
||||||
)
|
)
|
||||||
}
|
declarations += firJavaMethod
|
||||||
}.apply {
|
|
||||||
if (modality == Modality.SEALED) {
|
if (classIsAnnotation) {
|
||||||
sealedInheritors = javaClass.permittedTypes.mapNotNull { classifierType ->
|
val parameterForAnnotationConstructor = convertJavaAnnotationMethodToValueParameter(javaMethod, firJavaMethod)
|
||||||
val classifier = classifierType.classifier as? JavaClass
|
if (javaMethod.name == VALUE_METHOD_NAME) {
|
||||||
classifier?.let { JavaToKotlinClassMap.mapJavaToKotlin(it.fqName!!) }
|
valueParametersForAnnotationConstructor.valueParameterForValue = javaMethod to parameterForAnnotationConstructor
|
||||||
|
} else {
|
||||||
|
valueParametersForAnnotationConstructor.valueParameters[javaMethod] = parameterForAnnotationConstructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val javaClassDeclaredConstructors = javaClass.constructors
|
||||||
|
val constructorId = CallableId(classId.packageFqName, classId.relativeClassName, classId.shortClassName)
|
||||||
|
|
||||||
|
if (javaClassDeclaredConstructors.isEmpty()
|
||||||
|
&& javaClass.classKind == ClassKind.CLASS
|
||||||
|
&& javaClass.hasDefaultConstructor()
|
||||||
|
) {
|
||||||
|
declarations += convertJavaConstructorToFir(
|
||||||
|
javaConstructor = null,
|
||||||
|
constructorId,
|
||||||
|
javaClass,
|
||||||
|
ownerClassBuilder = this,
|
||||||
|
classTypeParameters,
|
||||||
|
javaTypeParameterStack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (javaConstructor in javaClassDeclaredConstructors) {
|
||||||
|
declarations += convertJavaConstructorToFir(
|
||||||
|
javaConstructor,
|
||||||
|
constructorId,
|
||||||
|
javaClass,
|
||||||
|
ownerClassBuilder = this,
|
||||||
|
classTypeParameters,
|
||||||
|
javaTypeParameterStack,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classKind == ClassKind.ENUM_CLASS) {
|
||||||
|
generateValuesFunction(
|
||||||
|
session,
|
||||||
|
classId.packageFqName,
|
||||||
|
classId.relativeClassName
|
||||||
|
)
|
||||||
|
generateValueOfFunction(session, classId.packageFqName, classId.relativeClassName)
|
||||||
|
}
|
||||||
|
if (classIsAnnotation) {
|
||||||
|
declarations +=
|
||||||
|
buildConstructorForAnnotationClass(
|
||||||
|
classSource = (javaClass as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement(FirFakeSourceElementKind.ImplicitConstructor) as? FirFakeSourceElement,
|
||||||
|
constructorId = constructorId,
|
||||||
|
ownerClassBuilder = this,
|
||||||
|
valueParametersForAnnotationConstructor = valueParametersForAnnotationConstructor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.apply {
|
||||||
|
if (modality == Modality.SEALED) {
|
||||||
|
sealedInheritors = javaClass.permittedTypes.mapNotNull { classifierType ->
|
||||||
|
val classifier = classifierType.classifier as? JavaClass
|
||||||
|
classifier?.let { JavaToKotlinClassMap.mapJavaToKotlin(it.fqName!!) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classIsAnnotation) {
|
||||||
|
// Cannot load these until the symbol is bound because they may be self-referential.
|
||||||
|
valueParametersForAnnotationConstructor.forEach { javaMethod, firValueParameter ->
|
||||||
|
javaMethod.annotationParameterDefaultValue?.let { javaDefaultValue ->
|
||||||
|
firValueParameter.defaultValue =
|
||||||
|
javaDefaultValue.toFirExpression(session, javaTypeParameterStack, firValueParameter.returnTypeRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,15 +423,13 @@ class JavaSymbolProvider(
|
|||||||
javaMethod: JavaMethod,
|
javaMethod: JavaMethod,
|
||||||
classId: ClassId,
|
classId: ClassId,
|
||||||
javaTypeParameterStack: JavaTypeParameterStack,
|
javaTypeParameterStack: JavaTypeParameterStack,
|
||||||
classIsAnnotation: Boolean,
|
|
||||||
valueParametersForAnnotationConstructor: ValueParametersForAnnotationConstructor,
|
|
||||||
dispatchReceiver: ConeClassLikeType
|
dispatchReceiver: ConeClassLikeType
|
||||||
): FirJavaMethod {
|
): FirJavaMethod {
|
||||||
val methodName = javaMethod.name
|
val methodName = javaMethod.name
|
||||||
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
|
val methodId = CallableId(classId.packageFqName, classId.relativeClassName, methodName)
|
||||||
val methodSymbol = FirNamedFunctionSymbol(methodId)
|
val methodSymbol = FirNamedFunctionSymbol(methodId)
|
||||||
val returnType = javaMethod.returnType
|
val returnType = javaMethod.returnType
|
||||||
val firJavaMethod = buildJavaMethod {
|
return buildJavaMethod {
|
||||||
session = this@JavaSymbolProvider.session
|
session = this@JavaSymbolProvider.session
|
||||||
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
|
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement()
|
||||||
symbol = methodSymbol
|
symbol = methodSymbol
|
||||||
@@ -448,27 +471,18 @@ class JavaSymbolProvider(
|
|||||||
containingClassAttr = ConeClassLikeLookupTagImpl(classId)
|
containingClassAttr = ConeClassLikeLookupTagImpl(classId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (classIsAnnotation) {
|
|
||||||
val parameterForAnnotationConstructor = buildJavaValueParameter {
|
|
||||||
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement(FirFakeSourceElementKind.ImplicitJavaAnnotationConstructor)
|
|
||||||
session = this@JavaSymbolProvider.session
|
|
||||||
returnTypeRef = firJavaMethod.returnTypeRef
|
|
||||||
name = methodName
|
|
||||||
javaMethod.annotationParameterDefaultValue?.let { javaDefaultValue ->
|
|
||||||
defaultValue = javaDefaultValue.toFirExpression(session, javaTypeParameterStack, returnTypeRef)
|
|
||||||
}
|
|
||||||
isVararg = returnType is JavaArrayType && methodName == VALUE_METHOD_NAME
|
|
||||||
annotationBuilder = { emptyList() }
|
|
||||||
}
|
|
||||||
if (methodName == VALUE_METHOD_NAME) {
|
|
||||||
valueParametersForAnnotationConstructor.valueParameterForValue = parameterForAnnotationConstructor
|
|
||||||
} else {
|
|
||||||
valueParametersForAnnotationConstructor.valueParameters += parameterForAnnotationConstructor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firJavaMethod
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun convertJavaAnnotationMethodToValueParameter(javaMethod: JavaMethod, firJavaMethod: FirJavaMethod): FirJavaValueParameter =
|
||||||
|
buildJavaValueParameter {
|
||||||
|
source = (javaMethod as? JavaElementImpl<*>)?.psi?.toFirPsiSourceElement(FirFakeSourceElementKind.ImplicitJavaAnnotationConstructor)
|
||||||
|
session = this@JavaSymbolProvider.session
|
||||||
|
returnTypeRef = firJavaMethod.returnTypeRef
|
||||||
|
name = javaMethod.name
|
||||||
|
isVararg = javaMethod.returnType is JavaArrayType && javaMethod.name == VALUE_METHOD_NAME
|
||||||
|
annotationBuilder = { emptyList() }
|
||||||
|
}
|
||||||
|
|
||||||
private fun convertJavaConstructorToFir(
|
private fun convertJavaConstructorToFir(
|
||||||
javaConstructor: JavaConstructor?,
|
javaConstructor: JavaConstructor?,
|
||||||
constructorId: CallableId,
|
constructorId: CallableId,
|
||||||
@@ -531,8 +545,7 @@ class JavaSymbolProvider(
|
|||||||
returnTypeRef = buildResolvedTypeRef {
|
returnTypeRef = buildResolvedTypeRef {
|
||||||
type = ownerClassBuilder.buildSelfTypeRef()
|
type = ownerClassBuilder.buildSelfTypeRef()
|
||||||
}
|
}
|
||||||
valueParameters.addIfNotNull(valueParametersForAnnotationConstructor.valueParameterForValue)
|
valueParametersForAnnotationConstructor.forEach { _, firValueParameter -> valueParameters += firValueParameter }
|
||||||
valueParameters += valueParametersForAnnotationConstructor.valueParameters
|
|
||||||
visibility = Visibilities.Public
|
visibility = Visibilities.Public
|
||||||
isInner = false
|
isInner = false
|
||||||
isPrimary = true
|
isPrimary = true
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
float e() default 1;
|
float e() default 1;
|
||||||
long j() default 1;
|
long j() default 1;
|
||||||
String f() default "default";
|
String f() default "default";
|
||||||
|
Class<?> g() default JavaAnn2.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODULE: main(lib)
|
// MODULE: main(lib)
|
||||||
@@ -47,6 +48,7 @@ fun box(): String {
|
|||||||
if (ann2.e != 1F) return "fail for e: expected = 1, but was ${ann2.e}"
|
if (ann2.e != 1F) return "fail for e: expected = 1, but was ${ann2.e}"
|
||||||
if (ann2.j != 1L) return "fail for j: expected = 1, but was ${ann2.j}"
|
if (ann2.j != 1L) return "fail for j: expected = 1, but was ${ann2.j}"
|
||||||
if (ann2.f != "default") return "fail for f: expected = default, but was ${ann2.f}"
|
if (ann2.f != "default") return "fail for f: expected = default, but was ${ann2.f}"
|
||||||
|
if (ann2.g != JavaAnn2::class) return "fail for g: expected = JavaAnn2, but was ${ann2.g}"
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user