Do not store ClassDescriptor in EnumValue

Only store the ClassId of the enum class and the Name of the entry, and
resolve the needed descriptor in getType() instead, which now takes the
module instance where that descriptor should be resolved
This commit is contained in:
Alexander Udalov
2017-12-29 16:45:34 +01:00
parent 9290d58ed0
commit 82574cb570
25 changed files with 99 additions and 167 deletions
@@ -438,7 +438,7 @@ public abstract class AnnotationCodegen {
}
@Nullable
private Set<ElementType> getJavaTargetList(ClassDescriptor descriptor) {
private static Set<ElementType> getJavaTargetList(ClassDescriptor descriptor) {
AnnotationDescriptor targetAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Target.class.getName()));
if (targetAnnotation != null) {
Collection<ConstantValue<?>> valueArguments = targetAnnotation.getAllValueArguments().values();
@@ -449,12 +449,9 @@ public abstract class AnnotationCodegen {
Set<ElementType> result = EnumSet.noneOf(ElementType.class);
for (ConstantValue<?> value : values) {
if (value instanceof EnumValue) {
ClassDescriptor enumEntry = ((EnumValue) value).getValue();
KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
if (classObjectType != null) {
if ("java/lang/annotation/ElementType".equals(typeMapper.mapType(classObjectType).getInternalName())) {
result.add(ElementType.valueOf(enumEntry.getName().asString()));
}
FqName enumClassFqName = ((EnumValue) value).getEnumClassId().asSingleFqName();
if (ElementType.class.getName().equals(enumClassFqName.asString())) {
result.add(ElementType.valueOf(((EnumValue) value).getEnumEntryName().asString()));
}
}
}
@@ -466,21 +463,18 @@ public abstract class AnnotationCodegen {
}
@NotNull
private RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
private static RetentionPolicy getRetentionPolicy(@NotNull Annotated descriptor) {
KotlinRetention retention = DescriptorUtilsKt.getAnnotationRetention(descriptor);
if (retention != null) {
return annotationRetentionMap.get(retention);
}
AnnotationDescriptor retentionAnnotation = descriptor.getAnnotations().findAnnotation(new FqName(Retention.class.getName()));
if (retentionAnnotation != null) {
ConstantValue<?> compileTimeConstant = CollectionsKt.firstOrNull(retentionAnnotation.getAllValueArguments().values());
if (compileTimeConstant instanceof EnumValue) {
ClassDescriptor enumEntry = ((EnumValue) compileTimeConstant).getValue();
KotlinType classObjectType = DescriptorUtilsKt.getClassValueType(enumEntry);
if (classObjectType != null) {
if ("java/lang/annotation/RetentionPolicy".equals(typeMapper.mapType(classObjectType).getInternalName())) {
return RetentionPolicy.valueOf(enumEntry.getName().asString());
}
ConstantValue<?> value = CollectionsKt.firstOrNull(retentionAnnotation.getAllValueArguments().values());
if (value instanceof EnumValue) {
FqName enumClassFqName = ((EnumValue) value).getEnumClassId().asSingleFqName();
if (RetentionPolicy.class.getName().equals(enumClassFqName.asString())) {
return RetentionPolicy.valueOf(((EnumValue) value).getEnumEntryName().asString());
}
}
}
@@ -59,16 +59,18 @@ class JvmStringTable(private val typeMapper: KotlinTypeMapper) : StringTable {
throw IllegalStateException("Cannot get FQ name of error class: " + descriptor)
}
// We use the following format to encode ClassId: "pkg/Outer.Inner".
// It represents a unique name, but such names don't usually appear in the constant pool, so we're writing "Lpkg/Outer$Inner;"
// instead and an instruction to drop the first and the last character in this string and replace all '$' with '.'.
// This works most of the time, except in two rare cases:
// - the name of the class or any of its outer classes contains dollars. In this case we're just storing the described
// string literally: "pkg/Outer.Inner$with$dollars"
// - the class is local or nested in local. In this case we're also storing the literal string, and also storing the fact that
// this name represents a local class in a separate list
return getClassIdIndex(descriptor.classId)
}
val classId = descriptor.classId
// We use the following format to encode ClassId: "pkg/Outer.Inner".
// It represents a unique name, but such names don't usually appear in the constant pool, so we're writing "Lpkg/Outer$Inner;"
// instead and an instruction to drop the first and the last character in this string and replace all '$' with '.'.
// This works most of the time, except in two rare cases:
// - the name of the class or any of its outer classes contains dollars. In this case we're just storing the described
// string literally: "pkg/Outer.Inner$with$dollars"
// - the class is local or nested in local. In this case we're also storing the literal string, and also storing the fact that
// this name represents a local class in a separate list
override fun getClassIdIndex(classId: ClassId): Int {
val string = classId.asString()
map[string]?.let { recordedIndex ->
@@ -108,11 +108,11 @@ public class MappingClassesForWhenByEnumCodegen {
v.putstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
for (Map.Entry<EnumValue, Integer> item : mapping.enumValuesToIntMapping()) {
EnumValue enumEntry = item.getKey();
EnumValue enumValue = item.getKey();
int mappedValue = item.getValue();
v.getstatic(cb.getThisName(), mapping.getFieldName(), MAPPINGS_FIELD_DESCRIPTOR);
v.getstatic(enumType.getInternalName(), enumEntry.getValue().getName().asString(), enumType.getDescriptor());
v.getstatic(enumType.getInternalName(), enumValue.getEnumEntryName().asString(), enumType.getDescriptor());
v.invokevirtual(enumType.getInternalName(), "ordinal", Type.getMethodDescriptor(Type.INT_TYPE), false);
v.iconst(mappedValue);
v.astore(Type.INT_TYPE);
@@ -208,7 +208,7 @@ class AnnotationChecker(
val valueArguments = targetEntryDescriptor.allValueArguments
val valueArgument = valueArguments.entries.firstOrNull()?.value as? ArrayValue ?: return null
return valueArgument.value.filterIsInstance<EnumValue>().mapNotNull {
KotlinTarget.valueOrNull(it.value.name.asString())
KotlinTarget.valueOrNull(it.enumEntryName.asString())
}.toSet()
}
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -662,7 +663,8 @@ private class ConstantExpressionEvaluatorVisitor(
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, expectedType: KotlinType?): CompileTimeConstant<*>? {
val enumDescriptor = trace.bindingContext.get(BindingContext.REFERENCE_TARGET, expression)
if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) {
return ConstantValueFactory.createEnumValue(enumDescriptor as ClassDescriptor).wrap()
val enumClassId = (enumDescriptor.containingDeclaration as ClassDescriptor).classId ?: return null
return EnumValue(enumClassId, enumDescriptor.name).wrap()
}
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
@@ -78,9 +78,8 @@ class AnnotationSerializer(private val stringTable: StringTable) {
override fun visitEnumValue(value: EnumValue, data: Unit) {
type = Type.ENUM
val enumEntry = value.value
classId = stringTable.getFqNameIndex(enumEntry.containingDeclaration as ClassDescriptor)
enumValueId = stringTable.getStringIndex(enumEntry.name.asString())
classId = stringTable.getClassIdIndex(value.enumClassId)
enumValueId = stringTable.getStringIndex(value.enumEntryName.asString())
}
override fun visitErrorValue(value: ErrorValue, data: Unit) {
@@ -644,19 +644,19 @@ class DescriptorSerializer private constructor(
proto.message = stringTable.getStringIndex(message)
}
val level = (args[RequireKotlinNames.LEVEL] as? EnumValue)?.value?.name?.asString()
val level = (args[RequireKotlinNames.LEVEL] as? EnumValue)?.enumEntryName?.asString()
when (level) {
DeprecationLevel.ERROR.toString() -> { /* ERROR is the default level */ }
DeprecationLevel.WARNING.toString() -> proto.level = ProtoBuf.VersionRequirement.Level.WARNING
DeprecationLevel.HIDDEN.toString() -> proto.level = ProtoBuf.VersionRequirement.Level.HIDDEN
DeprecationLevel.ERROR.name -> { /* ERROR is the default level */ }
DeprecationLevel.WARNING.name -> proto.level = ProtoBuf.VersionRequirement.Level.WARNING
DeprecationLevel.HIDDEN.name -> proto.level = ProtoBuf.VersionRequirement.Level.HIDDEN
}
val versionKind = (args[RequireKotlinNames.VERSION_KIND] as? EnumValue)?.value?.name?.asString()
val versionKind = (args[RequireKotlinNames.VERSION_KIND] as? EnumValue)?.enumEntryName?.asString()
when (versionKind) {
ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION.toString() -> { /* LANGUAGE_VERSION is the default kind */ }
ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION.toString() ->
ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION.name -> { /* LANGUAGE_VERSION is the default kind */ }
ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION.name ->
proto.versionKind = ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION
ProtoBuf.VersionRequirement.VersionKind.API_VERSION.toString() ->
ProtoBuf.VersionRequirement.VersionKind.API_VERSION.name ->
proto.versionKind = ProtoBuf.VersionRequirement.VersionKind.API_VERSION
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.serialization
import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters
import org.jetbrains.kotlin.name.ClassId
import java.io.OutputStream
interface StringTable {
@@ -24,5 +25,7 @@ interface StringTable {
fun getFqNameIndex(descriptor: ClassifierDescriptorWithTypeParameters): Int
fun getClassIdIndex(classId: ClassId): Int
fun serializeTo(output: OutputStream)
}
@@ -62,7 +62,7 @@ open class StringTableImpl : StringTable {
return getClassIdIndex(classId)
}
fun getClassIdIndex(classId: ClassId): Int {
override fun getClassIdIndex(classId: ClassId): Int {
val builder = QualifiedName.newBuilder()
builder.kind = QualifiedName.Kind.CLASS
@@ -5,7 +5,7 @@ public final annotation class Anno : kotlin.Annotation {
public final val e: test.E
}
@test.Anno(e = Unresolved enum entry: test/E.ENTRY) public open class Class {
@test.Anno(e = E.ENTRY) public open class Class {
public constructor Class()
}
@@ -169,8 +169,7 @@ class MultiModuleJavaAnalysisCustomTest : KtUsefulTestCase() {
it.allValueArguments.forEach {
val argument = it.value
if (argument is EnumValue) {
Assert.assertEquals("Enum entry name should be <module-name>X", "X", argument.value.name.identifier.last().toString())
checkDescriptor(argument.value, callable)
Assert.assertEquals("Enum entry name should be <module-name>X", "X", argument.enumEntryName.identifier.last().toString())
}
}
}