From b888d9e7d9a129dc26ec425bd7c7367edc1b487d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 23 Jun 2016 14:21:02 +0300 Subject: [PATCH] KT-2752: refactor NameSuggestion --- .../kotlin/js/naming/NameSuggestion.kt | 237 ++++++++---------- .../js/translate/utils/AnnotationsUtils.java | 44 +--- .../declaration/DelegationTranslator.kt | 4 +- 3 files changed, 122 insertions(+), 163 deletions(-) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt index f5cfe6d367f..bb5a84b8510 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt @@ -39,16 +39,16 @@ class NameSuggestion { private val cache: MutableMap = WeakHashMap() /** - * Generates names for declarations. Name consist of the following parts: + * Generates names for declarations. Name consists of the following parts: * * * Aliasing declaration, if the given `descriptor` does not have its own entity in JS. * * Scoping declaration. Declarations are usually compiled to the hierarchy of nested JS objects, * this attribute allows to find out where to put the declaration. * * Simple name, which is a name that object must (or may) get on the generated JS. - * * Whether the name is "shared". Shared names are visible to other modules and to native JS. - * In order to preserve ABI, these names must be persistent. Private names do not require particular - * name, so the code generator can invent any name which does not clash with anything; however, - * it may derive the name from the suggested name to improve readability and debugging. + * * Whether the name is stable. Stable names are visible to other modules and to native JS. + * Unstable names do not require particular name, so the code generator can invent any name + * which does not clash with anything; however, it may derive the name from the suggested name to + * improve readability and debugging experience. * * This method returns `null` for root declarations (modules and root packages). * It's guaranteed that a particular name is returned for any other declarations. @@ -71,7 +71,8 @@ class NameSuggestion { is PackageFragmentDescriptor -> { return if (!descriptor.name.isSpecial) { - SuggestedName(descriptor.fqName.pathSegments().map { it.asString() }, true, descriptor, descriptor.containingDeclaration) + SuggestedName(descriptor.fqName.pathSegments().map { it.asString() }, true, descriptor, + descriptor.containingDeclaration) } else { // Root packages are similar to modules @@ -97,21 +98,13 @@ class NameSuggestion { } } - val (localName, shared, parent) = getLocalName(descriptor) - return SuggestedName(listOf(localName), shared, descriptor, fixParent(parent)) + return generateDefault(descriptor) } - // Getters and setters have generation strategy similar to common declarations, except for they are declared as - // members of classes/packages, not corresponding properties. - private fun fixParent(parent: DeclarationDescriptor) = when (parent) { - is PropertyDescriptor -> parent.containingDeclaration - else -> parent - } - - private fun getLocalName(descriptor: DeclarationDescriptor): LocalName { + private fun generateDefault(descriptor: DeclarationDescriptor): SuggestedName { // Dynamic declarations always require shared names as defined in Kotlin source code if (descriptor.isDynamic()) { - return LocalName(descriptor.name.asString(), true, descriptor.containingDeclaration!!) + return SuggestedName(listOf(descriptor.name.asString()), true, descriptor, descriptor.containingDeclaration!!) } // For any non-local declaration suggest its own suggested name and put it in scope of its containing declaration. @@ -144,12 +137,17 @@ class NameSuggestion { } } while (current is FunctionDescriptor) - parts.reverse() - val (id, shared) = getMangledName(parts.joinToString("$"), descriptor) - return LocalName(id, shared, current) - } + // Getters and setters have generation strategy similar to common declarations, except for they are declared as + // members of classes/packages, not corresponding properties. + if (current is PropertyDescriptor) { + current = current.containingDeclaration + } - private data class LocalName(val id: String, val shared: Boolean, val parent: DeclarationDescriptor) + parts.reverse() + val unmangledName = parts.joinToString("$") + val (id, stable) = getMangledName(unmangledName, descriptor) + return SuggestedName(listOf(id), stable, descriptor, current) + } // For regular names suggest its string representation // For property accessors suggest name of a property with 'get_' and 'set_' prefixes @@ -168,114 +166,95 @@ class NameSuggestion { } } - private fun getMangledName(baseName: String, descriptor: DeclarationDescriptor): Pair { - if (descriptor !is CallableDescriptor) { - if (isNativeObject(descriptor) || isLibraryObject(descriptor)) { - return Pair(getNameForAnnotatedObjectWithOverrides(descriptor) ?: descriptor.name.asString(), true) - } - return Pair(baseName, needsStableMangling(descriptor)) - } - - var resolvedDescriptor: CallableDescriptor = descriptor - var overriddenDescriptor: CallableDescriptor? = descriptor - while (overriddenDescriptor != null) { - resolvedDescriptor = overriddenDescriptor - if (isNativeObject(resolvedDescriptor) || isLibraryObject(resolvedDescriptor)) { - val explicitName = getNameForAnnotatedObjectWithOverrides(resolvedDescriptor) - if (explicitName != null) { - return Pair(explicitName, true) - } - } - overriddenDescriptor = getOverriddenDescriptor(overriddenDescriptor)?.original - } - - when { - isNativeObject(resolvedDescriptor) || isLibraryObject(resolvedDescriptor) -> { - return Pair(getNameForAnnotatedObjectWithOverrides(resolvedDescriptor) ?: resolvedDescriptor.name.asString(), true) - } - } - - val explicitName = getJsName(resolvedDescriptor) - return when { - explicitName != null -> Pair(explicitName, true) - needsStableMangling(resolvedDescriptor) -> - Pair(getStableMangledName(baseName, getArgumentTypesAsString(resolvedDescriptor)), true) - else -> Pair(baseName, false) - } - } - - private fun getOverriddenDescriptor(functionDescriptor: CallableDescriptor): CallableDescriptor? { - val overriddenDescriptors = functionDescriptor.overriddenDescriptors - if (overriddenDescriptors.isEmpty()) { - return null - } - - //TODO: for now translator can't deal with multiple inheritance good enough - return overriddenDescriptors.iterator().next() - } - - private fun getArgumentTypesAsString(descriptor: CallableDescriptor): String { - val argTypes = StringBuilder() - - val receiverParameter = descriptor.extensionReceiverParameter - if (receiverParameter != null) { - argTypes.append(receiverParameter.type.getJetTypeFqName(true)).append(".") - } - - argTypes.append(descriptor.valueParameters.joinToString(",") { it.type.getJetTypeFqName(true) }) - - return argTypes.toString() - } - - private fun getStableMangledName(suggestedName: String, forCalculateId: String): String { - val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$" - return suggestedName + suffix - } - - private fun needsStableMangling(descriptor: DeclarationDescriptor): Boolean { - if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return false - if (descriptor is ClassOrPackageFragmentDescriptor) return true - if (descriptor !is CallableMemberDescriptor) return false - - // Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration - // for avoid clashing names when inheritance. - if (DescriptorUtils.isOverride(descriptor)) return true - - val containingDeclaration = descriptor.containingDeclaration - if (isNativeObject(containingDeclaration) || isLibraryObject(containingDeclaration)) return true - - return when (containingDeclaration) { - is PackageFragmentDescriptor -> descriptor.visibility.isPublicAPI - is ClassDescriptor -> { - if (descriptor.modality == Modality.OPEN || descriptor.modality == Modality.ABSTRACT || - containingDeclaration.modality == Modality.OPEN || containingDeclaration.modality == Modality.ABSTRACT - ) { - return descriptor.visibility.isPublicAPI - } - - // valueOf() is created in the library with a mangled name for every enum class - if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return true - - // Don't use stable mangling when it inside a non-public API declaration. - if (!containingDeclaration.visibility.isPublicAPI) return false - - // Ignore the `protected` visibility because it can be use outside a containing declaration - // only when the containing declaration is overridable. - if (descriptor.visibility === Visibilities.PUBLIC) return true - - return false - } - else -> { - assert(containingDeclaration is CallableMemberDescriptor) { - "containingDeclaration for descriptor have unsupported type for mangling, " + - "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration - } - false - } - } - } - companion object { + @JvmStatic private fun getMangledName(baseName: String, descriptor: DeclarationDescriptor): Pair { + // If we have a callable descriptor (property or method) it can override method in a parent class. + // Traverse to the topmost overridden method. + // It does not matter which path to choose during traversal, since front-end must ensure + // that names required by different overridden method do no differ. + val overriddenDescriptor = if (descriptor is CallableDescriptor) { + generateSequence(descriptor) { it.overriddenDescriptors.firstOrNull()?.original }.last() + } + else { + descriptor + } + + // If declaration is marked with either @native, @library or @JsName, return its stable name as is. + val nativeName = getNameForAnnotatedObject(overriddenDescriptor) + if (nativeName != null) return Pair(nativeName, true) + + val stable = shouldBeStable(descriptor) + val finalName = if (overriddenDescriptor is CallableDescriptor && stable) { + getStableMangledName(baseName, getArgumentTypesAsString(overriddenDescriptor)) + } + else { + baseName + } + return Pair(finalName, stable) + } + + @JvmStatic fun getArgumentTypesAsString(descriptor: CallableDescriptor): String { + val argTypes = StringBuilder() + + val receiverParameter = descriptor.extensionReceiverParameter + if (receiverParameter != null) { + argTypes.append(receiverParameter.type.getJetTypeFqName(true)).append(".") + } + + argTypes.append(descriptor.valueParameters.joinToString(",") { it.type.getJetTypeFqName(true) }) + + return argTypes.toString() + } + + @JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String { + val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$" + return suggestedName + suffix + } + + private fun shouldBeStable(descriptor: DeclarationDescriptor): Boolean { + if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return false + if (descriptor is ClassOrPackageFragmentDescriptor) return true + if (descriptor !is CallableMemberDescriptor) return false + + // Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration + // for avoid clashing names when inheritance. + if (DescriptorUtils.isOverride(descriptor)) return true + + val containingDeclaration = descriptor.containingDeclaration + if (isNativeObject(containingDeclaration) || isLibraryObject(containingDeclaration)) return true + + return when (containingDeclaration) { + is PackageFragmentDescriptor -> descriptor.visibility.isPublicAPI + is ClassDescriptor -> { + // Open (abstract) public methods of classes or final public methods of open (abstract) classes should be stable + if (descriptor.modality == Modality.OPEN || descriptor.modality == Modality.ABSTRACT || + containingDeclaration.modality == Modality.OPEN || containingDeclaration.modality == Modality.ABSTRACT + ) { + return descriptor.visibility.isPublicAPI + } + + // valueOf() is created in the library with a mangled name for every enum class + if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return true + + // Don't use stable mangling when it inside a non-public class. + if (!containingDeclaration.visibility.isPublicAPI) return false + + // Ignore the `protected` visibility because it can be use outside a containing declaration + // only when the containing declaration is overridable. + if (descriptor.visibility === Visibilities.PUBLIC) return true + + return false + } + else -> { + assert(containingDeclaration is CallableMemberDescriptor) { + "containingDeclaration for descriptor have unsupported type for mangling, " + + "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration + } + false + } + } + } + @JvmStatic fun mangledId(forCalculateId: String): String { val absHashCode = Math.abs(forCalculateId.hashCode()) return if (absHashCode != 0) Integer.toString(absHashCode, Character.MAX_RADIX) else "" diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java index 495c94af67c..46ac44d2377 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/translate/utils/AnnotationsUtils.java @@ -68,7 +68,7 @@ public final class AnnotationsUtils { } @Nullable - public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor declarationDescriptor, + private static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor declarationDescriptor, @NotNull PredefinedAnnotation annotation) { if (!hasAnnotation(declarationDescriptor, annotation)) { return null; @@ -77,39 +77,19 @@ public final class AnnotationsUtils { } @Nullable - public static String getNameForAnnotatedObjectWithOverrides(@NotNull DeclarationDescriptor declarationDescriptor) { - List descriptors; - - if (declarationDescriptor instanceof CallableMemberDescriptor && - DescriptorUtils.isOverride((CallableMemberDescriptor) declarationDescriptor)) { - - Set overriddenDeclarations = - DescriptorUtils.getAllOverriddenDeclarations((CallableMemberDescriptor) declarationDescriptor); - - descriptors = ContainerUtil.mapNotNull(overriddenDeclarations, new Function() { - @Override - public DeclarationDescriptor fun(CallableMemberDescriptor descriptor) { - return DescriptorUtils.isOverride(descriptor) ? null : descriptor; - } - }); - } - else { - descriptors = ContainerUtil.newArrayList(declarationDescriptor); - } - - for (DeclarationDescriptor descriptor : descriptors) { - for (PredefinedAnnotation annotation : PredefinedAnnotation.Companion.getWITH_CUSTOM_NAME()) { - if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) { - continue; - } - String name = getNameForAnnotatedObject(descriptor, annotation); - if (name == null) { - name = getJsName(descriptor); - } - return name != null ? name : descriptor.getName().asString(); + public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor descriptor) { + for (PredefinedAnnotation annotation : PredefinedAnnotation.Companion.getWITH_CUSTOM_NAME()) { + if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) { + continue; } + String name = getNameForAnnotatedObject(descriptor, annotation); + if (name == null) { + name = getJsName(descriptor); + } + return name != null ? name : descriptor.getName().asString(); } - return null; + + return getJsName(descriptor); } @Nullable diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt index 2db77831c64..c6f02351415 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/DelegationTranslator.kt @@ -63,8 +63,8 @@ class DelegationTranslator( else { val classFqName = DescriptorUtils.getFqName(classDescriptor) val typeFqName = DescriptorUtils.getFqName(descriptor) - val suffix = NameSuggestion.mangledId("${classFqName.asString()}:${typeFqName.asString()}") - val suggestedName = Namer.getDelegatePrefix() + if (suffix.isNotEmpty()) "_$suffix\$" else "" + val idForMangling = "${classFqName.asString()}:${typeFqName.asString()}" + val suggestedName = NameSuggestion.getStableMangledName(Namer.getDelegatePrefix(), idForMangling) val delegateName = context.getScopeForDescriptor(classDescriptor).declareFreshName("${suggestedName}_0") fields[specifier] = Field(delegateName, true) }