From b1c46652463a21adbcb8ea9256b29c90ae236c5e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 7 Nov 2013 17:51:04 +0400 Subject: [PATCH] Support enums in Java code --- .../descriptors/LazyJavaClassDescriptor.kt | 33 +++++++-- .../descriptors/LazyJavaClassMemberScope.kt | 45 +++++------- .../lazy/descriptors/LazyJavaMemberScope.kt | 68 ++++++++++++++----- .../java/resolver/JavaClassResolver.java | 4 +- 4 files changed, 99 insertions(+), 51 deletions(-) diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt index f86690357c1..718b2a2d3ee 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassDescriptor.kt @@ -28,6 +28,12 @@ import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor +import org.jetbrains.kotlin.util.iif +import org.jetbrains.jet.lang.resolve.java.descriptor.JavaEnumClassObjectDescriptor +import org.jetbrains.jet.lang.descriptors.Modality +import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl +import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler +import org.jetbrains.jet.lang.resolve.scopes.WritableScope class LazyJavaClassDescriptor( private val c: LazyJavaResolverContextWithTypes, @@ -62,12 +68,31 @@ class LazyJavaClassDescriptor( override fun getUnsubstitutedPrimaryConstructor(): ConstructorDescriptor? = null + private val _classObjectDescriptor = c.storageManager.createNullableLazyValue { + if (jClass.isEnum()) { + val classObject = JavaEnumClassObjectDescriptor(this) + classObject.setModality(Modality.FINAL) + classObject.setVisibility(jClass.getVisibility()) + classObject.setTypeParameterDescriptors(Collections.emptyList()) + classObject.createTypeConstructor() + + val scope = LazyJavaClassMemberScope(innerC, classObject, jClass, enumClassObject = true) + val writableScope = WritableScopeImpl(scope, classObject, RedeclarationHandler.THROW_EXCEPTION, "Enum class object scope") + writableScope.changeLockLevel(WritableScope.LockLevel.BOTH) + + classObject.setScopeForMemberLookup(writableScope) + + JavaClassResolver.createEnumSyntheticMethods(classObject, this.getDefaultType()) + + classObject + } + else null + } + override fun getClassObjectDescriptor(): ClassDescriptor? = _classObjectDescriptor() + override fun getClassObjectType(): JetType? = getClassObjectDescriptor()?.let { d -> d.getDefaultType() } + override fun getConstructors() = _scopeForMemberLookup._constructors() - override fun getClassObjectType(): JetType? = null - - override fun getClassObjectDescriptor(): ClassDescriptor? = null - private val _annotations = c.storageManager.createLazyValue { c.resolveAnnotations(jClass.getAnnotations()) } override fun getAnnotations(): List = _annotations() diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassMemberScope.kt index ad090406cca..96da1f80d2a 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -17,49 +17,38 @@ package org.jetbrains.jet.lang.resolve.java.lazy.descriptors import org.jetbrains.jet.lang.descriptors.* -import org.jetbrains.jet.storage.NotNullLazyValue -import org.jetbrains.jet.lang.resolve.name.LabelName import org.jetbrains.jet.lang.resolve.name.Name -import org.jetbrains.jet.lang.resolve.scopes.JetScope -import org.jetbrains.jet.utils.emptyList import org.jetbrains.jet.lang.resolve.java.structure.JavaClass import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod -import org.jetbrains.jet.lang.resolve.java.structure.JavaField import org.jetbrains.jet.lang.resolve.java.lazy.LazyJavaResolverContextWithTypes -import org.jetbrains.jet.lang.resolve.java.descriptor.JavaMethodDescriptor -import org.jetbrains.jet.lang.resolve.DescriptorUtils -import org.jetbrains.jet.lang.resolve.java.lazy.child import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl -import org.jetbrains.jet.lang.resolve.java.lazy.resolveAnnotations import org.jetbrains.jet.lang.resolve.java.structure.JavaArrayType import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage -import org.jetbrains.jet.lang.types.TypeUtils -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns -import org.jetbrains.jet.lang.resolve.java.lazy.hasNotNullAnnotation -import org.jetbrains.jet.lang.resolve.java.lazy.types.LazyJavaTypeAttributes -import org.jetbrains.jet.lang.resolve.java.lazy.hasMutableAnnotation import org.jetbrains.kotlin.util.iif -import org.jetbrains.jet.lang.resolve.java.lazy.hasReadOnlyAnnotation -import org.jetbrains.jet.utils.valuesToMap -import org.jetbrains.jet.lang.resolve.java.structure.JavaValueParameter import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl import java.util.Collections -import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor import org.jetbrains.jet.lang.resolve.java.resolver.JavaConstructorResolver import org.jetbrains.jet.utils.* import java.util.ArrayList import org.jetbrains.jet.lang.resolve.java.lazy.types.toAttributes -import org.jetbrains.jet.lang.types.JetType +import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils +import org.jetbrains.jet.lang.resolve.java.structure.JavaMember +import org.jetbrains.jet.lang.resolve.DescriptorUtils +import org.jetbrains.jet.lang.descriptors.impl.EnumEntrySyntheticClassDescriptor +import org.jetbrains.jet.storage.get public class LazyJavaClassMemberScope( c: LazyJavaResolverContextWithTypes, - containingDeclaration: LazyJavaClassDescriptor, - private val jClass: JavaClass + containingDeclaration: ClassDescriptor, + private val jClass: JavaClass, + private val enumClassObject: Boolean = false ) : LazyJavaMemberScope(c, containingDeclaration) { - override fun computeMemberIndex(): MemberIndex = object : ClassMemberIndex(jClass, { m -> !m.isStatic() }) { - // For SAM-constructors - override fun getAllMetodNames(): Collection = super.getAllMetodNames() + getAllClassNames() + override fun computeMemberIndex(): MemberIndex { + return object : ClassMemberIndex(jClass, { !enumClassObject && !it.isStatic() }) { + // For SAM-constructors + override fun getAllMetodNames(): Collection = super.getAllMetodNames() + getAllClassNames() + } } internal val _constructors = c.storageManager.createLazyValue { @@ -165,10 +154,10 @@ public class LazyJavaClassMemberScope( if (jNestedClass == null) null else - LazyJavaClassDescriptor(c, getContainingDeclaration(), getContainingDeclaration().fqName.child(name), jNestedClass) + LazyJavaClassDescriptor(c, getContainingDeclaration(), jNestedClass.getFqName()!!, jNestedClass) } - override fun getClassifier(name: Name): ClassifierDescriptor? = nestedClasses(name) + override fun getClassifier(name: Name): ClassifierDescriptor? = if (enumClassObject) null else nestedClasses(name) override fun getAllClassNames(): Collection = nestedClassIndex().keySet() override fun addExtraDescriptors(result: MutableCollection) { @@ -179,8 +168,8 @@ public class LazyJavaClassMemberScope( override fun getImplicitReceiversHierarchy(): List = listOf() - override fun getContainingDeclaration(): LazyJavaClassDescriptor { - return super.getContainingDeclaration() as LazyJavaClassDescriptor + override fun getContainingDeclaration(): ClassDescriptor { + return super.getContainingDeclaration() as ClassDescriptor } // namespaces should be resolved elsewhere diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaMemberScope.kt index 5208aa242b9..1b6e7a560c0 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/lazy/descriptors/LazyJavaMemberScope.kt @@ -41,6 +41,8 @@ import org.jetbrains.annotations.TestOnly import org.jetbrains.jet.utils.Printer import org.jetbrains.jet.lang.resolve.java.resolver.ExternalSignatureResolver import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils +import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptorForObject +import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl public abstract class LazyJavaMemberScope( protected val c: LazyJavaResolverContextWithTypes, @@ -67,15 +69,19 @@ public abstract class LazyJavaMemberScope( (name: Name): Collection -> val methods = memberIndex().findMethodsByName(name) - val functions = LinkedHashSet(methods.flatMap { - m -> - val function = resolveMethodToFunctionDescriptor(m, true) - val samAdapter = JavaFunctionResolver.resolveSamAdapter(function) - if (samAdapter != null) - listOf(function, samAdapter) - else - listOf(function) - }) + val functions = LinkedHashSet( + methods.iterator() + // values() and valueOf() are added manually, see LazyJavaClassDescriptor::getClassObjectDescriptor() + .filter{ m -> !DescriptorResolverUtils.shouldBeInEnumClassObject(m) } + .flatMap { + m -> + val function = resolveMethodToFunctionDescriptor(m, true) + val samAdapter = JavaFunctionResolver.resolveSamAdapter(function) + if (samAdapter != null) + listOf(function, samAdapter).iterator() + else + listOf(function).iterator() + }.toList()) if (_containingDeclaration is NamespaceDescriptor) { val klass = JavaFunctionResolver.findClassInNamespace(_containingDeclaration, name); @@ -211,7 +217,9 @@ public abstract class LazyJavaMemberScope( val field = memberIndex().findFieldByName(name) if (field != null) { - properties.add(resolveProperty(field)) + if (DescriptorResolverUtils.shouldBeInEnumClassObject(field) == DescriptorUtils.isEnumClassObject(_containingDeclaration)) { + properties.add(resolveProperty(field)) + } } if (_containingDeclaration is ClassDescriptor) { @@ -227,13 +235,7 @@ public abstract class LazyJavaMemberScope( private fun resolveProperty(field: JavaField): PropertyDescriptor { val isVar = !field.isFinal() - val propertyDescriptor = JavaPropertyDescriptor( - getContainingDeclaration(), - c.resolveAnnotations(field.getAnnotations()), - field.getVisibility(), - isVar, - field.getName() - ) + val propertyDescriptor = createPropertyDescriptor(field) propertyDescriptor.initialize(null, null) val propertyType = getPropertyType(field) @@ -250,6 +252,38 @@ public abstract class LazyJavaMemberScope( return propertyDescriptor } + private fun createPropertyDescriptor(field: JavaField): PropertyDescriptorImpl { + val isVar = !field.isFinal() + val visibility = field.getVisibility() + val annotations = c.resolveAnnotations(field.getAnnotations()) + val propertyName = field.getName() + + if (field.isEnumEntry()) { + assert(!isVar, "Enum entries should be immutable.") + assert(DescriptorUtils.isEnumClassObject(_containingDeclaration), "Enum entries should be put into class object of enum only: " + _containingDeclaration) + //TODO: this is a hack to indicate that this enum entry is an object + // class descriptor for enum entries is not used by backends so for now this should be safe to use + val dummyClassDescriptorForEnumEntryObject = ClassDescriptorImpl(_containingDeclaration, Collections.emptyList(), Modality.FINAL, propertyName) + dummyClassDescriptorForEnumEntryObject.initialize( + true, + Collections.emptyList(), + Collections.emptyList(), + JetScope.EMPTY, + Collections.emptySet(), + null, + false) + return JavaPropertyDescriptorForObject( + _containingDeclaration as ClassDescriptor, + annotations, + visibility, + propertyName, + dummyClassDescriptorForEnumEntryObject + ) + } + + return JavaPropertyDescriptor(_containingDeclaration, annotations, visibility, isVar, propertyName) + } + private fun getPropertyType(field: JavaField): JetType { // Fields do not have their own generic parameters val propertyType = c.typeResolver.transformJavaType(field.getType(), LazyJavaTypeAttributes(c, field, TypeUsage.MEMBER_SIGNATURE_INVARIANT)) diff --git a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java index 5a08c42f859..480ac61e36e 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java +++ b/core/descriptor.loader.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java @@ -305,7 +305,7 @@ public final class JavaClassResolver { } @NotNull - private static FqNameUnsafe getFqNameForClassObject(@NotNull JavaClass javaClass) { + public static FqNameUnsafe getFqNameForClassObject(@NotNull JavaClass javaClass) { FqName fqName = javaClass.getFqName(); assert fqName != null : "Reading java class with no qualified name"; return fqName.toUnsafe().child(getClassObjectName(javaClass.getName())); @@ -414,7 +414,7 @@ public final class JavaClassResolver { return javaClass.getOuterClass() != null && !javaClass.isStatic(); } - private static void createEnumSyntheticMethods(@NotNull JavaEnumClassObjectDescriptor classObject, @NotNull JetType enumType) { + public static void createEnumSyntheticMethods(@NotNull JavaEnumClassObjectDescriptor classObject, @NotNull JetType enumType) { JetType valuesReturnType = KotlinBuiltIns.getInstance().getArrayType(enumType); SimpleFunctionDescriptor valuesMethod = DescriptorFactory.createEnumClassObjectValuesMethod(classObject, valuesReturnType); classObject.getBuilder().addFunctionDescriptor(valuesMethod);