JavaDescriptorResolver: represent Java enums as kotlin enums.

Split members from java enum psiclass into two groups: for class itself and for class object:
Hack JavaClassMembersScope to be able to use it for static members.
Hack JavaDescriptorResolverHelper to process fields for enums read from class files.

Introduce JavaDescriptorResolver#ResolverEnumClassObjectClassData.

Specially treat values and valueOf methods.
This commit is contained in:
Natalia.Ukhorskaya
2012-08-21 16:23:30 +04:00
parent d28e59ac37
commit 8b8c595f4e
3 changed files with 130 additions and 37 deletions
@@ -29,11 +29,7 @@ import org.jetbrains.jet.lang.resolve.name.LabelName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* Class static of instance members.
@@ -51,9 +47,6 @@ public class JavaClassMembersScope extends JavaClassOrPackageScope {
if (resolverScopeData.psiClass == null) {
throw new IllegalArgumentException("must pass PsiClass here");
}
if (resolverScopeData.staticMembers) {
throw new IllegalArgumentException("static members should be resolved using " + JavaPackageScope.class);
}
}
@NotNull
@@ -35,7 +35,9 @@ import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation;
import org.jetbrains.jet.lang.resolve.java.kt.PsiAnnotationWithFlags;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.*;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
import org.jetbrains.jet.rt.signature.JetSignatureAdapter;
@@ -47,6 +49,8 @@ import org.jetbrains.jet.utils.ExceptionUtils;
import javax.inject.Inject;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValueOfMethod;
import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValuesMethod;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
/**
@@ -198,6 +202,18 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
}
static class ResolverEnumClassObjectClassData extends ResolverClassData {
protected ResolverEnumClassObjectClassData(
@Nullable PsiClass psiClass,
@NotNull FqName fqName,
@NotNull ClassDescriptorFromJvmBytecode descriptor
) {
super(psiClass, null, fqName, true, descriptor);
}
}
/** Either package or class with static members */
static class ResolverNamespaceData extends ResolverScopeData {
private final NamespaceDescriptor namespaceDescriptor;
@@ -540,13 +556,17 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
*/
@Nullable
private MutableClassDescriptorLite createClassObjectDescriptor(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) {
checkPsiClassIsNotJet(psiClass);
if (psiClass.isEnum()) {
return createClassObjectDescriptorForEnum(containing, psiClass);
}
PsiClass classObjectPsiClass = getInnerClassClassObject(psiClass);
if (classObjectPsiClass == null) {
return null;
}
checkPsiClassIsNotJet(psiClass);
FqName fqName = new FqName(classObjectPsiClass.getQualifiedName());
ResolverClassData classData = new ClassDescriptorFromJvmBytecode(
containing, ClassKind.OBJECT, classObjectPsiClass, fqName, this)
@@ -554,15 +574,47 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
classDescriptorCache.put(fqName, classData);
classData.classDescriptor.setSupertypes(getSupertypes(new PsiClassWrapper(classObjectPsiClass), classData, new ArrayList<TypeParameterDescriptor>(0)));
classData.classDescriptor.setName(getClassObjectName(containing.getName()));
classData.classDescriptor.setModality(Modality.FINAL);
classData.classDescriptor.setVisibility(containing.getVisibility());
classData.classDescriptor.setTypeParameterDescriptors(new ArrayList<TypeParameterDescriptor>(0));
classData.classDescriptor.createTypeConstructor();
classData.classDescriptor.setScopeForMemberLookup(new JavaClassMembersScope(semanticServices, classData));
ClassDescriptorFromJvmBytecode classObjectDescriptor = classData.classDescriptor;
classObjectDescriptor.setSupertypes(
getSupertypes(new PsiClassWrapper(classObjectPsiClass), classData, new ArrayList<TypeParameterDescriptor>(0)));
setUpClassObjectDescriptor(containing, fqName, classData, getClassObjectName(containing.getName()));
return classObjectDescriptor;
}
return classData.classDescriptor;
@NotNull
private MutableClassDescriptorLite createClassObjectDescriptorForEnum(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) {
String psiClassQualifiedName = psiClass.getQualifiedName();
assert psiClassQualifiedName != null : "Reading java class with no qualified name";
FqName fqName = new FqName(psiClassQualifiedName + "." + getClassObjectName(psiClass.getName()).getName());
ClassDescriptorFromJvmBytecode classObjectDescriptor = new ClassDescriptorFromJvmBytecode(
containing, ClassKind.OBJECT, psiClass, fqName, this);
ResolverEnumClassObjectClassData data = new ResolverEnumClassObjectClassData(psiClass, fqName, classObjectDescriptor);
setUpClassObjectDescriptor(containing, fqName, data, getClassObjectName(containing.getName().getName()));
classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValuesMethod(classObjectDescriptor, trace));
classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValueOfMethod(classObjectDescriptor, trace));
return classObjectDescriptor;
}
private void setUpClassObjectDescriptor(
@NotNull ClassDescriptor containing,
@NotNull FqName fqName,
@NotNull ResolverClassData data,
@NotNull Name classObjectName
) {
ClassDescriptorFromJvmBytecode classDescriptor = data.classDescriptor;
classDescriptorCache.put(fqName, data);
classDescriptor.setName(classObjectName);
classDescriptor.setModality(Modality.FINAL);
classDescriptor.setVisibility(containing.getVisibility());
classDescriptor.setTypeParameterDescriptors(Collections.<TypeParameterDescriptor>emptyList());
classDescriptor.createTypeConstructor();
JavaClassMembersScope classMembersScope = new JavaClassMembersScope(semanticServices, data);
WritableScopeImpl writableScope = new WritableScopeImpl(classMembersScope, classDescriptor, RedeclarationHandler.THROW_EXCEPTION, fqName.getFqName());
writableScope.changeLockLevel(WritableScope.LockLevel.BOTH);
classDescriptor.setScopeForMemberLookup(writableScope);
}
static boolean isJavaLangObject(JetType type) {
@@ -756,7 +808,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
}
psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE);
if (psiClass != null) {
if (psiClass != null && !psiClass.isEnum()) {
trace.record(JavaBindingContext.JAVA_NAMESPACE_KIND, ns, JavaNamespaceKind.CLASS_STATICS);
break lookingForPsi;
}
@@ -1106,8 +1158,11 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
visibility = resolveVisibility(anyMember.getMember().psiMember,
((PsiMethodWrapper) members.getter.getMember()).getJetMethod());
}
DeclarationDescriptor realOwner = getRealOwner(owner, scopeData, anyMember.getMember().isStatic());
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
owner,
realOwner,
resolveAnnotations(anyMember.getMember().psiMember),
resolveModality(anyMember.getMember(), isFinal),
visibility,
@@ -1194,7 +1249,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
propertyDescriptor.setType(
propertyType,
typeParameters,
DescriptorUtils.getExpectedThisObjectIfNeeded(owner),
DescriptorUtils.getExpectedThisObjectIfNeeded(realOwner),
receiverType
);
if (getterDescriptor != null) {
@@ -1236,6 +1291,21 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
namedMembers.propertyDescriptors = properties;
}
@NotNull
private ClassOrNamespaceDescriptor getRealOwner(
@NotNull ClassOrNamespaceDescriptor owner,
@NotNull ResolverScopeData scopeData,
boolean isStatic
) {
boolean isEnum = scopeData.psiClass.isEnum();
if (isEnum && isStatic) {
return resolveClass(new FqName(scopeData.psiClass.getQualifiedName())).getClassObjectDescriptor();
}
else {
return owner;
}
}
private void resolveNamedGroupFunctions(@NotNull ClassOrNamespaceDescriptor owner, PsiClass psiClass,
TypeSubstitutor typeSubstitutorForGenericSuperclasses, NamedMembers namedMembers, Name methodName, ResolverScopeData scopeData) {
if (namedMembers.functionDescriptors != null) {
@@ -1248,9 +1318,9 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
Set<SimpleFunctionDescriptor> functionsFromCurrent = Sets.newHashSet();
for (PsiMethodWrapper method : namedMembers.methods) {
FunctionDescriptorImpl function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, tempTrace);
SimpleFunctionDescriptor function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, tempTrace);
if (function != null) {
functionsFromCurrent.add((SimpleFunctionDescriptor) function);
functionsFromCurrent.add(function);
}
}
@@ -1276,6 +1346,14 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
functions.addAll(functionsFromCurrent);
if (isEnumClassObject(owner)) {
for (FunctionDescriptor functionDescriptor : Lists.newArrayList(functions)) {
if (isEnumSpecialMethod(functionDescriptor)) {
functions.remove(functionDescriptor);
}
}
}
try {
namedMembers.functionDescriptors = functions;
tempTrace.commit();
@@ -1369,7 +1447,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
}
@Nullable
private FunctionDescriptorImpl resolveMethodToFunctionDescriptor(
private SimpleFunctionDescriptor resolveMethodToFunctionDescriptor(
@NotNull final PsiClass psiClass, final PsiMethodWrapper method,
@NotNull ResolverScopeData scopeData, BindingTrace tempTrace) {
@@ -1432,11 +1510,27 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
BindingContextUtils.recordFunctionDeclarationToDescriptor(tempTrace, method.getPsiMethod(), functionDescriptorImpl);
FunctionDescriptor substitutedFunctionDescriptor = functionDescriptorImpl;
if (method.getPsiMethod().getContainingClass() != psiClass && !method.isStatic()) {
throw new IllegalStateException("non-static method in subclass");
}
return (FunctionDescriptorImpl) substitutedFunctionDescriptor;
return functionDescriptorImpl;
}
private boolean isEnumSpecialMethod(@NotNull FunctionDescriptor functionDescriptor) {
List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
String methodName = functionDescriptor.getName().getName();
JetType nullableString = TypeUtils.makeNullable(JetStandardLibrary.getInstance().getStringType());
if (methodName.equals("valueOf") && methodTypeParameters.size() == 1
&& JetTypeChecker.INSTANCE.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString)) {
return true;
}
return (methodName.equals("values") && methodTypeParameters.isEmpty());
}
private static boolean isEnumClassObject(@NotNull DeclarationDescriptor classObjectDescriptor) {
DeclarationDescriptor containingDeclaration = classObjectDescriptor.getContainingDeclaration();
return ((containingDeclaration instanceof ClassDescriptor) &&
((ClassDescriptor) containingDeclaration).getKind() == ClassKind.ENUM_CLASS);
}
private List<AnnotationDescriptor> resolveAnnotations(PsiModifierListOwner owner, @NotNull List<Runnable> tasks) {
@@ -64,6 +64,10 @@ class JavaDescriptorResolverHelper {
}
private boolean includeMember(PsiMemberWrapper member) {
if (psiClass.getPsiClass().isEnum() && staticMembers) {
return member.isStatic();
}
if (member.isStatic() != staticMembers) {
return false;
}
@@ -80,20 +84,22 @@ class JavaDescriptorResolverHelper {
}
private void processFields() {
if (!kotlin) {
for (PsiField field0 : psiClass.getPsiClass().getAllFields()) {
PsiFieldWrapper field = new PsiFieldWrapper(field0);
// Hack to load static members for enum class loaded from class file
if (kotlin && !psiClass.getPsiClass().isEnum()) {
return;
}
for (PsiField field0 : psiClass.getPsiClass().getAllFields()) {
PsiFieldWrapper field = new PsiFieldWrapper(field0);
// group must be created even for excluded field
NamedMembers namedMembers = getNamedMembers(Name.identifier(field.getName()));
// group must be created even for excluded field
NamedMembers namedMembers = getNamedMembers(Name.identifier(field.getName()));
if (!includeMember(field)) {
continue;
}
TypeSource type = new TypeSource("", field.getType(), field0);
namedMembers.addPropertyAccessor(new PropertyAccessorData(field, type, null));
if (!includeMember(field)) {
continue;
}
TypeSource type = new TypeSource("", field.getType(), field0);
namedMembers.addPropertyAccessor(new PropertyAccessorData(field, type, null));
}
}