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:
+1
-8
@@ -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.name.Name;
|
||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class static of instance members.
|
* Class static of instance members.
|
||||||
@@ -51,9 +47,6 @@ public class JavaClassMembersScope extends JavaClassOrPackageScope {
|
|||||||
if (resolverScopeData.psiClass == null) {
|
if (resolverScopeData.psiClass == null) {
|
||||||
throw new IllegalArgumentException("must pass PsiClass here");
|
throw new IllegalArgumentException("must pass PsiClass here");
|
||||||
}
|
}
|
||||||
if (resolverScopeData.staticMembers) {
|
|
||||||
throw new IllegalArgumentException("static members should be resolved using " + JavaPackageScope.class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+112
-18
@@ -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.java.kt.PsiAnnotationWithFlags;
|
||||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
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.*;
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
import org.jetbrains.jet.lang.types.lang.JetStandardClasses;
|
||||||
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
||||||
import org.jetbrains.jet.rt.signature.JetSignatureAdapter;
|
import org.jetbrains.jet.rt.signature.JetSignatureAdapter;
|
||||||
@@ -47,6 +49,8 @@ import org.jetbrains.jet.utils.ExceptionUtils;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.*;
|
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;
|
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 */
|
/** Either package or class with static members */
|
||||||
static class ResolverNamespaceData extends ResolverScopeData {
|
static class ResolverNamespaceData extends ResolverScopeData {
|
||||||
private final NamespaceDescriptor namespaceDescriptor;
|
private final NamespaceDescriptor namespaceDescriptor;
|
||||||
@@ -540,13 +556,17 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private MutableClassDescriptorLite createClassObjectDescriptor(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) {
|
private MutableClassDescriptorLite createClassObjectDescriptor(@NotNull ClassDescriptor containing, @NotNull PsiClass psiClass) {
|
||||||
|
checkPsiClassIsNotJet(psiClass);
|
||||||
|
|
||||||
|
if (psiClass.isEnum()) {
|
||||||
|
return createClassObjectDescriptorForEnum(containing, psiClass);
|
||||||
|
}
|
||||||
|
|
||||||
PsiClass classObjectPsiClass = getInnerClassClassObject(psiClass);
|
PsiClass classObjectPsiClass = getInnerClassClassObject(psiClass);
|
||||||
if (classObjectPsiClass == null) {
|
if (classObjectPsiClass == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPsiClassIsNotJet(psiClass);
|
|
||||||
|
|
||||||
FqName fqName = new FqName(classObjectPsiClass.getQualifiedName());
|
FqName fqName = new FqName(classObjectPsiClass.getQualifiedName());
|
||||||
ResolverClassData classData = new ClassDescriptorFromJvmBytecode(
|
ResolverClassData classData = new ClassDescriptorFromJvmBytecode(
|
||||||
containing, ClassKind.OBJECT, classObjectPsiClass, fqName, this)
|
containing, ClassKind.OBJECT, classObjectPsiClass, fqName, this)
|
||||||
@@ -554,15 +574,47 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
|
|
||||||
classDescriptorCache.put(fqName, classData);
|
classDescriptorCache.put(fqName, classData);
|
||||||
|
|
||||||
classData.classDescriptor.setSupertypes(getSupertypes(new PsiClassWrapper(classObjectPsiClass), classData, new ArrayList<TypeParameterDescriptor>(0)));
|
ClassDescriptorFromJvmBytecode classObjectDescriptor = classData.classDescriptor;
|
||||||
classData.classDescriptor.setName(getClassObjectName(containing.getName()));
|
classObjectDescriptor.setSupertypes(
|
||||||
classData.classDescriptor.setModality(Modality.FINAL);
|
getSupertypes(new PsiClassWrapper(classObjectPsiClass), classData, new ArrayList<TypeParameterDescriptor>(0)));
|
||||||
classData.classDescriptor.setVisibility(containing.getVisibility());
|
setUpClassObjectDescriptor(containing, fqName, classData, getClassObjectName(containing.getName()));
|
||||||
classData.classDescriptor.setTypeParameterDescriptors(new ArrayList<TypeParameterDescriptor>(0));
|
return classObjectDescriptor;
|
||||||
classData.classDescriptor.createTypeConstructor();
|
}
|
||||||
classData.classDescriptor.setScopeForMemberLookup(new JavaClassMembersScope(semanticServices, classData));
|
|
||||||
|
|
||||||
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) {
|
static boolean isJavaLangObject(JetType type) {
|
||||||
@@ -756,7 +808,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
psiClass = psiClassFinder.findPsiClass(fqName, PsiClassFinder.RuntimeClassesHandleMode.IGNORE);
|
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);
|
trace.record(JavaBindingContext.JAVA_NAMESPACE_KIND, ns, JavaNamespaceKind.CLASS_STATICS);
|
||||||
break lookingForPsi;
|
break lookingForPsi;
|
||||||
}
|
}
|
||||||
@@ -1106,8 +1158,11 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
visibility = resolveVisibility(anyMember.getMember().psiMember,
|
visibility = resolveVisibility(anyMember.getMember().psiMember,
|
||||||
((PsiMethodWrapper) members.getter.getMember()).getJetMethod());
|
((PsiMethodWrapper) members.getter.getMember()).getJetMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeclarationDescriptor realOwner = getRealOwner(owner, scopeData, anyMember.getMember().isStatic());
|
||||||
|
|
||||||
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
|
PropertyDescriptor propertyDescriptor = new PropertyDescriptor(
|
||||||
owner,
|
realOwner,
|
||||||
resolveAnnotations(anyMember.getMember().psiMember),
|
resolveAnnotations(anyMember.getMember().psiMember),
|
||||||
resolveModality(anyMember.getMember(), isFinal),
|
resolveModality(anyMember.getMember(), isFinal),
|
||||||
visibility,
|
visibility,
|
||||||
@@ -1194,7 +1249,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
propertyDescriptor.setType(
|
propertyDescriptor.setType(
|
||||||
propertyType,
|
propertyType,
|
||||||
typeParameters,
|
typeParameters,
|
||||||
DescriptorUtils.getExpectedThisObjectIfNeeded(owner),
|
DescriptorUtils.getExpectedThisObjectIfNeeded(realOwner),
|
||||||
receiverType
|
receiverType
|
||||||
);
|
);
|
||||||
if (getterDescriptor != null) {
|
if (getterDescriptor != null) {
|
||||||
@@ -1236,6 +1291,21 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
namedMembers.propertyDescriptors = properties;
|
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,
|
private void resolveNamedGroupFunctions(@NotNull ClassOrNamespaceDescriptor owner, PsiClass psiClass,
|
||||||
TypeSubstitutor typeSubstitutorForGenericSuperclasses, NamedMembers namedMembers, Name methodName, ResolverScopeData scopeData) {
|
TypeSubstitutor typeSubstitutorForGenericSuperclasses, NamedMembers namedMembers, Name methodName, ResolverScopeData scopeData) {
|
||||||
if (namedMembers.functionDescriptors != null) {
|
if (namedMembers.functionDescriptors != null) {
|
||||||
@@ -1248,9 +1318,9 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
|
|
||||||
Set<SimpleFunctionDescriptor> functionsFromCurrent = Sets.newHashSet();
|
Set<SimpleFunctionDescriptor> functionsFromCurrent = Sets.newHashSet();
|
||||||
for (PsiMethodWrapper method : namedMembers.methods) {
|
for (PsiMethodWrapper method : namedMembers.methods) {
|
||||||
FunctionDescriptorImpl function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, tempTrace);
|
SimpleFunctionDescriptor function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, tempTrace);
|
||||||
if (function != null) {
|
if (function != null) {
|
||||||
functionsFromCurrent.add((SimpleFunctionDescriptor) function);
|
functionsFromCurrent.add(function);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1276,6 +1346,14 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
|
|
||||||
functions.addAll(functionsFromCurrent);
|
functions.addAll(functionsFromCurrent);
|
||||||
|
|
||||||
|
if (isEnumClassObject(owner)) {
|
||||||
|
for (FunctionDescriptor functionDescriptor : Lists.newArrayList(functions)) {
|
||||||
|
if (isEnumSpecialMethod(functionDescriptor)) {
|
||||||
|
functions.remove(functionDescriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
namedMembers.functionDescriptors = functions;
|
namedMembers.functionDescriptors = functions;
|
||||||
tempTrace.commit();
|
tempTrace.commit();
|
||||||
@@ -1369,7 +1447,7 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private FunctionDescriptorImpl resolveMethodToFunctionDescriptor(
|
private SimpleFunctionDescriptor resolveMethodToFunctionDescriptor(
|
||||||
@NotNull final PsiClass psiClass, final PsiMethodWrapper method,
|
@NotNull final PsiClass psiClass, final PsiMethodWrapper method,
|
||||||
@NotNull ResolverScopeData scopeData, BindingTrace tempTrace) {
|
@NotNull ResolverScopeData scopeData, BindingTrace tempTrace) {
|
||||||
|
|
||||||
@@ -1432,11 +1510,27 @@ public class JavaDescriptorResolver implements DependencyClassByQualifiedNameRes
|
|||||||
|
|
||||||
BindingContextUtils.recordFunctionDeclarationToDescriptor(tempTrace, method.getPsiMethod(), functionDescriptorImpl);
|
BindingContextUtils.recordFunctionDeclarationToDescriptor(tempTrace, method.getPsiMethod(), functionDescriptorImpl);
|
||||||
|
|
||||||
FunctionDescriptor substitutedFunctionDescriptor = functionDescriptorImpl;
|
|
||||||
if (method.getPsiMethod().getContainingClass() != psiClass && !method.isStatic()) {
|
if (method.getPsiMethod().getContainingClass() != psiClass && !method.isStatic()) {
|
||||||
throw new IllegalStateException("non-static method in subclass");
|
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) {
|
private List<AnnotationDescriptor> resolveAnnotations(PsiModifierListOwner owner, @NotNull List<Runnable> tasks) {
|
||||||
|
|||||||
+17
-11
@@ -64,6 +64,10 @@ class JavaDescriptorResolverHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean includeMember(PsiMemberWrapper member) {
|
private boolean includeMember(PsiMemberWrapper member) {
|
||||||
|
if (psiClass.getPsiClass().isEnum() && staticMembers) {
|
||||||
|
return member.isStatic();
|
||||||
|
}
|
||||||
|
|
||||||
if (member.isStatic() != staticMembers) {
|
if (member.isStatic() != staticMembers) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -80,20 +84,22 @@ class JavaDescriptorResolverHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processFields() {
|
private void processFields() {
|
||||||
if (!kotlin) {
|
// Hack to load static members for enum class loaded from class file
|
||||||
for (PsiField field0 : psiClass.getPsiClass().getAllFields()) {
|
if (kotlin && !psiClass.getPsiClass().isEnum()) {
|
||||||
PsiFieldWrapper field = new PsiFieldWrapper(field0);
|
return;
|
||||||
|
}
|
||||||
|
for (PsiField field0 : psiClass.getPsiClass().getAllFields()) {
|
||||||
|
PsiFieldWrapper field = new PsiFieldWrapper(field0);
|
||||||
|
|
||||||
// group must be created even for excluded field
|
// group must be created even for excluded field
|
||||||
NamedMembers namedMembers = getNamedMembers(Name.identifier(field.getName()));
|
NamedMembers namedMembers = getNamedMembers(Name.identifier(field.getName()));
|
||||||
|
|
||||||
if (!includeMember(field)) {
|
if (!includeMember(field)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
TypeSource type = new TypeSource("", field.getType(), field0);
|
|
||||||
namedMembers.addPropertyAccessor(new PropertyAccessorData(field, type, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeSource type = new TypeSource("", field.getType(), field0);
|
||||||
|
namedMembers.addPropertyAccessor(new PropertyAccessorData(field, type, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user