JS: update support of enum's values() and valueOf()

This commit is contained in:
Alexander Udalov
2014-09-05 18:55:23 +04:00
parent c5345a83c9
commit d52eff0825
10 changed files with 111 additions and 94 deletions
@@ -28,6 +28,8 @@ import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.*;
@@ -170,4 +172,18 @@ public class CodegenUtil {
private static boolean rawTypeMatches(JetType type, ClassifierDescriptor classifier) {
return type.getConstructor().getDeclarationDescriptor().getOriginal() == classifier.getOriginal();
}
public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
return "valueOf".equals(functionDescriptor.getName().asString())
&& methodTypeParameters.size() == 1
&& JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
}
public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
return "values".equals(functionDescriptor.getName().asString())
&& methodTypeParameters.isEmpty();
}
}
@@ -928,7 +928,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
FunctionDescriptor valuesFunction = findEnumFunction("values", new Function1<FunctionDescriptor, Boolean>() {
@Override
public Boolean invoke(FunctionDescriptor descriptor) {
return isEnumValuesMethod(descriptor);
return CodegenUtil.isEnumValuesMethod(descriptor);
}
});
MethodVisitor mv = v.newMethod(OtherOrigin(myClass, valuesFunction), ACC_PUBLIC | ACC_STATIC, "values", "()" + type.getDescriptor(), null, null);
@@ -946,7 +946,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
FunctionDescriptor valueOfFunction = findEnumFunction("valueOf", new Function1<FunctionDescriptor, Boolean>() {
@Override
public Boolean invoke(FunctionDescriptor descriptor) {
return isEnumValueOfMethod(descriptor);
return CodegenUtil.isEnumValueOfMethod(descriptor);
}
});
MethodVisitor mv = v.newMethod(OtherOrigin(myClass, valueOfFunction),
@@ -44,8 +44,6 @@ import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalPackageFragm
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.io.File;
@@ -281,20 +279,6 @@ public class JvmCodegenUtil {
return null;
}
public static boolean isEnumValueOfMethod(@NotNull FunctionDescriptor functionDescriptor) {
List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
JetType nullableString = TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getStringType());
return "valueOf".equals(functionDescriptor.getName().asString())
&& methodTypeParameters.size() == 1
&& JetTypeChecker.DEFAULT.isSubtypeOf(methodTypeParameters.get(0).getType(), nullableString);
}
public static boolean isEnumValuesMethod(@NotNull FunctionDescriptor functionDescriptor) {
List<ValueParameterDescriptor> methodTypeParameters = functionDescriptor.getValueParameters();
return "values".equals(functionDescriptor.getName().asString())
&& methodTypeParameters.isEmpty();
}
@NotNull
public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) {
return descriptor instanceof PropertyAccessorDescriptor
@@ -19,7 +19,7 @@ package org.jetbrains.jet.codegen.intrinsics;
import com.google.common.collect.ImmutableList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.JvmCodegenUtil;
import org.jetbrains.jet.backend.common.CodegenUtil;
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
@@ -203,11 +203,11 @@ public class IntrinsicMethods {
DeclarationDescriptor container = descriptor.getContainingDeclaration();
//noinspection ConstantConditions
if (isClassObject(container) && isEnumClass(container.getContainingDeclaration())) {
if (JvmCodegenUtil.isEnumValuesMethod(functionDescriptor)) {
if (CodegenUtil.isEnumValuesMethod(functionDescriptor)) {
return ENUM_VALUES;
}
if (JvmCodegenUtil.isEnumValueOfMethod(functionDescriptor)) {
if (CodegenUtil.isEnumValueOfMethod(functionDescriptor)) {
return ENUM_VALUE_OF;
}
}
@@ -19,13 +19,10 @@ package org.jetbrains.k2js.translate.context;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ClassKind;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.plugin.JetLanguage;
import static org.jetbrains.jet.lang.descriptors.ClassKind.TRAIT;
/**
* Encapuslates different types of constants and naming conventions.
*/
@@ -40,9 +37,9 @@ public final class Namer {
private static final String APPLY_FUNCTION = "apply";
private static final String CLASS_OBJECT_NAME = "createClass";
private static final String ENUM_CLASS_OBJECT_NAME = "createEnumClass";
private static final String TRAIT_OBJECT_NAME = "createTrait";
private static final String OBJECT_OBJECT_NAME = "createObject";
private static final String ENUM_ENTRIES_NAME = "createEnumEntries";
private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction";
private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction";
private static final String CALLABLE_REF_FOR_CONSTRUCTOR_NAME = "getCallableRefForConstructor";
@@ -178,6 +175,8 @@ public final class Namer {
@NotNull
private final JsName className;
@NotNull
private final JsName enumClassName;
@NotNull
private final JsName traitName;
@NotNull
private final JsExpression definePackage;
@@ -186,8 +185,6 @@ public final class Namer {
@NotNull
private final JsName objectName;
@NotNull
private final JsName enumEntriesName;
@NotNull
private final JsName callableRefForMemberFunctionName;
@NotNull
private final JsName callableRefForExtensionFunctionName;
@@ -221,7 +218,7 @@ public final class Namer {
callSetProperty = kotlin("callSetter");
className = kotlinScope.declareName(CLASS_OBJECT_NAME);
enumEntriesName = kotlinScope.declareName(ENUM_ENTRIES_NAME);
enumClassName = kotlinScope.declareName(ENUM_CLASS_OBJECT_NAME);
objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
@@ -241,8 +238,8 @@ public final class Namer {
}
@NotNull
public JsExpression enumEntriesCreationMethodReference() {
return kotlin(enumEntriesName);
public JsExpression enumClassCreationMethodReference() {
return kotlin(enumClassName);
}
@NotNull
@@ -321,7 +318,7 @@ public final class Namer {
}
@NotNull
/*package*/ JsScope getKotlinScope() {
/*package*/ JsScope getKotlinScope() {
return kotlinScope;
}
@@ -332,21 +329,24 @@ public final class Namer {
@NotNull
public JsExpression classCreateInvocation(@NotNull ClassDescriptor descriptor) {
ClassKind kind = descriptor.getKind();
if (kind == TRAIT) {
return traitCreationMethodReference();
switch (descriptor.getKind()) {
case TRAIT:
return traitCreationMethodReference();
case ENUM_CLASS:
return enumClassCreationMethodReference();
case ENUM_ENTRY:
case OBJECT:
case CLASS_OBJECT:
return objectCreationMethodReference();
case ANNOTATION_CLASS:
throw new IllegalStateException("Can't create annotation class instance: " + descriptor);
case CLASS:
return DescriptorUtils.isAnonymousObject(descriptor)
? objectCreationMethodReference()
: classCreationMethodReference();
default:
throw new UnsupportedOperationException("Unsupported class kind: " + descriptor);
}
if (kind.isSingleton() || DescriptorUtils.isAnonymousObject(descriptor)) {
return objectCreationMethodReference();
}
return classCreationMethodReference();
}
@NotNull
public JsInvocation enumEntriesObjectCreateInvocation() {
return new JsInvocation(enumEntriesCreationMethodReference());
}
@NotNull
@@ -37,7 +37,6 @@ import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.Map;
import static org.jetbrains.jet.lang.descriptors.ClassKind.CLASS_OBJECT;
import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
import static org.jetbrains.k2js.translate.utils.AnnotationsUtils.*;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
@@ -502,12 +501,26 @@ public final class StaticContext {
return null;
}
};
Rule<JsNameRef> staticMembersHaveContainerQualifier = new Rule<JsNameRef>() {
@Override
public JsNameRef apply(@NotNull DeclarationDescriptor descriptor) {
if (descriptor instanceof CallableDescriptor && !isNativeObject(descriptor)) {
CallableDescriptor callableDescriptor = (CallableDescriptor) descriptor;
if (DescriptorUtils.isStaticDeclaration(callableDescriptor)) {
return getQualifiedReference(callableDescriptor.getContainingDeclaration());
}
}
return null;
}
};
addRule(libraryObjectsHaveKotlinQualifier);
addRule(constructorOrClassObjectHasTheSameQualifierAsTheClass);
addRule(standardObjectsHaveKotlinQualifier);
addRule(packageLevelDeclarationsHaveEnclosingPackagesNamesAsQualifier);
addRule(nativeObjectsHaveNativePartOfFullQualifier);
addRule(staticMembersHaveContainerQualifier);
}
}
@@ -42,11 +42,9 @@ import java.util.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.*;
import static org.jetbrains.jet.lang.types.TypeUtils.topologicallySortSuperclassesAndRecordAllInstances;
import static org.jetbrains.k2js.translate.initializer.InitializerUtils.createClassObjectInitializer;
import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
@@ -135,12 +133,17 @@ public final class ClassTranslator extends AbstractTranslator {
DeclarationBodyVisitor bodyVisitor = new DeclarationBodyVisitor(properties, staticProperties);
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
delegationTranslator.generateDelegated(properties);
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
new JsDataClassGenerator(classDeclaration, declarationContext, properties).generate();
}
if (isEnumClass(descriptor)) {
JsObjectLiteral enumEntries = new JsObjectLiteral(bodyVisitor.getEnumEntryList(), true);
JsFunction function = simpleReturnFunction(declarationContext.getScopeForDescriptor(descriptor), enumEntries);
invocationArguments.add(function);
}
boolean hasStaticProperties = !staticProperties.isEmpty();
if (!properties.isEmpty() || hasStaticProperties) {
if (properties.isEmpty()) {
@@ -158,6 +161,7 @@ public final class ClassTranslator extends AbstractTranslator {
invocationArguments.add(new JsDocComment(JsAstUtils.LENDS_JS_DOC_TAG, qualifiedReference));
invocationArguments.add(new JsObjectLiteral(staticProperties, true));
}
return invocationArguments;
}
@@ -181,21 +185,6 @@ public final class ClassTranslator extends AbstractTranslator {
return declarationContext;
}
private void mayBeAddEnumEntry(@NotNull List<JsPropertyInitializer> enumEntryList,
@NotNull List<JsPropertyInitializer> staticProperties,
@NotNull TranslationContext declarationContext
) {
if (descriptor.getKind() == ClassKind.ENUM_CLASS) {
JsInvocation invocation = context().namer().enumEntriesObjectCreateInvocation();
invocation.getArguments().add(new JsObjectLiteral(enumEntryList, true));
JsFunction fun = simpleReturnFunction(declarationContext.getScopeForDescriptor(descriptor), invocation);
staticProperties.add(createClassObjectInitializer(fun, declarationContext));
} else {
assert enumEntryList.isEmpty(): "Only enum class may have enum entry. Class kind is: " + descriptor.getKind();
}
}
private JsExpression getSuperclassReferences(@NotNull TranslationContext declarationContext) {
List<JsExpression> superClassReferences = getSupertypesNameReferences();
if (superClassReferences.isEmpty()) {
@@ -22,10 +22,10 @@ import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.backend.common.CodegenUtil;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.OverrideResolver;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
@@ -41,7 +41,8 @@ import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isAnonymousObject;
import static org.jetbrains.k2js.translate.context.Namer.getKotlinBackingFieldName;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
public final class TranslationUtils {
public static final Comparator<FunctionDescriptor> OVERLOADED_FUNCTION_COMPARATOR = new OverloadedFunctionComparator();
@@ -177,11 +178,16 @@ public final class TranslationUtils {
else if (containingDeclaration instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
// Use stable mangling when it inside a overridable declaration for avoid clashing names when inheritance.
// Use stable mangling when it's inside an overridable declaration to avoid clashing names on inheritance.
if (classDescriptor.getModality().isOverridable()) {
return true;
}
// valueOf() is created in the library with a mangled name for every enum class
if (CodegenUtil.isEnumValueOfMethod(descriptor)) {
return true;
}
// Don't use stable mangling when it inside a non-public API declaration.
if (!classDescriptor.getVisibility().isPublicAPI()) {
return false;
+1 -27
View File
@@ -231,34 +231,8 @@
toString: function () {
return this.name();
}
});
(function () {
function valueOf(name) {
return this[name];
}
function getValues() {
return this.values$;
}
Kotlin.createEnumEntries = function (enumEntryList) {
var i = 0;
var values = [];
for (var entryName in enumEntryList) {
if (enumEntryList.hasOwnProperty(entryName)) {
var entryObject = enumEntryList[entryName];
values[i] = entryObject;
entryObject.ordinal$ = i;
entryObject.name$ = entryName;
i++;
}
}
enumEntryList.values$ = values;
enumEntryList.valueOf_61zpoe$ = valueOf;
enumEntryList.values = getValues;
return enumEntryList;
};
})();
);
Kotlin.PropertyMetadata = Kotlin.createClassNow(null,
function (name) {
@@ -222,6 +222,41 @@ var Kotlin = {};
return $o;
};
/**
* @param {(function():Array.<*>)|null} basesFun
* @param {?=} constructor
* @param {function():Object} enumEntries
* @param {Object=} properties
* @returns {*}
*/
Kotlin.createEnumClass = function (basesFun, constructor, enumEntries, properties) {
return Kotlin.createClass(
basesFun, constructor, properties, {
object_initializer$: function () {
var enumEntryList = enumEntries()
var i = 0;
var values = [];
for (var entryName in enumEntryList) {
if (enumEntryList.hasOwnProperty(entryName)) {
var entryObject = enumEntryList[entryName];
values[i] = entryObject;
entryObject.ordinal$ = i;
entryObject.name$ = entryName;
i++;
}
}
enumEntryList.values$ = values;
return enumEntryList;
},
values: function() {
return this.object.values$;
},
valueOf_61zpoe$: function(name) {
return this.object[name];
}
})
};
/**
* @param {(function():Array.<*>)|null} basesFun
* @param {Object=} properties