From d52eff0825f187093f95d9225c13cf084785ad08 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 5 Sep 2014 18:55:23 +0400 Subject: [PATCH] JS: update support of enum's values() and valueOf() --- .../jet/backend/common/CodegenUtil.java | 16 +++++++ .../codegen/ImplementationBodyCodegen.java | 4 +- .../jetbrains/jet/codegen/JvmCodegenUtil.java | 16 ------- .../codegen/intrinsics/IntrinsicMethods.java | 6 +-- .../k2js/translate/context/Namer.java | 48 +++++++++---------- .../k2js/translate/context/StaticContext.java | 15 +++++- .../declaration/ClassTranslator.java | 25 +++------- .../translate/utils/TranslationUtils.java | 12 +++-- js/js.translator/testData/kotlin_lib.js | 28 +---------- js/js.translator/testData/kotlin_lib_ecma5.js | 35 ++++++++++++++ 10 files changed, 111 insertions(+), 94 deletions(-) diff --git a/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java index f2b57a06e6c..c6980d8324a 100644 --- a/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java +++ b/compiler/backend-common/src/org/jetbrains/jet/backend/common/CodegenUtil.java @@ -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 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 methodTypeParameters = functionDescriptor.getValueParameters(); + return "values".equals(functionDescriptor.getName().asString()) + && methodTypeParameters.isEmpty(); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index b177e21677c..fe53b1a87ec 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -928,7 +928,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { FunctionDescriptor valuesFunction = findEnumFunction("values", new Function1() { @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() { @Override public Boolean invoke(FunctionDescriptor descriptor) { - return isEnumValueOfMethod(descriptor); + return CodegenUtil.isEnumValueOfMethod(descriptor); } }); MethodVisitor mv = v.newMethod(OtherOrigin(myClass, valueOfFunction), diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/JvmCodegenUtil.java index a2f1dcbb8e3..72f84221699 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/JvmCodegenUtil.java @@ -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 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 methodTypeParameters = functionDescriptor.getValueParameters(); - return "values".equals(functionDescriptor.getName().asString()) - && methodTypeParameters.isEmpty(); - } - @NotNull public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { return descriptor instanceof PropertyAccessorDescriptor diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java index 25864f197be..83dd41bde76 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java @@ -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; } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java index 636f54f5247..cada21faeb4 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java @@ -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 diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java index 50031749922..e344bcf7309 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/StaticContext.java @@ -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 staticMembersHaveContainerQualifier = new Rule() { + @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); } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java index 79aac87c198..123eb62f625 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/declaration/ClassTranslator.java @@ -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 enumEntryList, - @NotNull List 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 superClassReferences = getSupertypesNameReferences(); if (superClassReferences.isEmpty()) { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index 03680c4e817..f6adb5604ec 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -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 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; diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index db18e3f8478..d03e407a868 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -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) { diff --git a/js/js.translator/testData/kotlin_lib_ecma5.js b/js/js.translator/testData/kotlin_lib_ecma5.js index 0954fb2689f..9493dfedfd6 100644 --- a/js/js.translator/testData/kotlin_lib_ecma5.js +++ b/js/js.translator/testData/kotlin_lib_ecma5.js @@ -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