KT-2752: refactor NameSuggestion, change rules for determining name stability and applying mangling

This commit is contained in:
Alexey Andreev
2016-09-21 16:17:49 +03:00
parent 6f7e7d8504
commit 9c7c82b151
21 changed files with 190 additions and 273 deletions
@@ -66,6 +66,11 @@ class NameSuggestion {
return suggest(descriptor.containingDeclaration!!)
}
// Dynamic declarations always require stable names as defined in Kotlin source code
if (descriptor.isDynamic()) {
return SuggestedName(listOf(descriptor.name.asString()), true, descriptor, descriptor.containingDeclaration!!)
}
when (descriptor) {
// Modules are root declarations, we don't produce declarations for them, therefore they can't clash
is ModuleDescriptor -> return null
@@ -94,8 +99,8 @@ class NameSuggestion {
// Local functions and variables are always private with their own names as suggested names
is CallableDescriptor ->
if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) {
val name = getMangledName(getSuggestedName(descriptor), descriptor)
return SuggestedName(listOf(name.first), false, descriptor, descriptor.containingDeclaration)
val name = getNameForAnnotatedObject(descriptor) ?: getSuggestedName(descriptor)
return SuggestedName(listOf(name), false, descriptor, descriptor.containingDeclaration)
}
}
@@ -103,11 +108,6 @@ class NameSuggestion {
}
private fun generateDefault(descriptor: DeclarationDescriptor): SuggestedName {
// Dynamic declarations always require stable names as defined in Kotlin source code
if (descriptor.isDynamic()) {
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.
// For local declaration get a sequence for names of all containing functions and join their names with '$' symbol,
// and use container of topmost function, i.e.
@@ -146,7 +146,7 @@ class NameSuggestion {
parts.reverse()
val unmangledName = parts.joinToString("$")
val (id, stable) = getMangledName(unmangledName, descriptor)
val (id, stable) = mangleNameIfNecessary(unmangledName, descriptor)
return SuggestedName(listOf(id), stable, descriptor, current)
}
@@ -168,7 +168,7 @@ class NameSuggestion {
}
companion object {
private fun getMangledName(baseName: String, descriptor: DeclarationDescriptor): Pair<String, Boolean> {
private fun mangleNameIfNecessary(baseName: String, descriptor: DeclarationDescriptor): NameAndStability {
// 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
@@ -182,20 +182,64 @@ class NameSuggestion {
// 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)
if (nativeName != null) return NameAndStability(nativeName, true)
val stable = shouldBeStable(descriptor)
val finalName = when {
overriddenDescriptor is CallableDescriptor && stable -> {
getStableMangledName(baseName, getArgumentTypesAsString(overriddenDescriptor))
}
shouldMangleUnstable(overriddenDescriptor) -> getPrivateMangledName(baseName, overriddenDescriptor as CallableDescriptor)
else -> baseName
return mangleRegularNameIfNecessary(baseName, overriddenDescriptor)
}
private fun mangleRegularNameIfNecessary(baseName: String, descriptor: DeclarationDescriptor): NameAndStability {
if (descriptor is ClassOrPackageFragmentDescriptor) {
return NameAndStability(baseName, !DescriptorUtils.isDescriptorWithLocalVisibility(descriptor))
}
return Pair(finalName, stable)
fun regularAndUnstable() = NameAndStability(baseName, false)
if (descriptor !is CallableMemberDescriptor) {
// Actually, only reified types get here, and it would be properly to put assertion here
// However, it's better to generate wrong code than crash
return regularAndUnstable()
}
fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, getArgumentTypesAsString(descriptor)), true)
fun mangledPrivate() = NameAndStability(getPrivateMangledName(baseName, descriptor), false)
val containingDeclaration = descriptor.containingDeclaration
return when (containingDeclaration) {
is PackageFragmentDescriptor -> if (descriptor.visibility.isPublicAPI) mangledAndStable() else regularAndUnstable()
is ClassDescriptor -> {
// valueOf() is created in the library with a mangled name for every enum class
if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return mangledAndStable()
// Make all public declarations stable
if (descriptor.visibility == Visibilities.PUBLIC) return mangledAndStable()
// Make all protected declarations of non-final public classes stable
if (descriptor.visibility == Visibilities.PROTECTED &&
!containingDeclaration.isFinalClass &&
containingDeclaration.visibility.isPublicAPI
) {
return mangledAndStable()
}
// Mangle (but make unstable) all non-public API of public classes
if (containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass) {
return mangledPrivate()
}
regularAndUnstable()
}
else -> {
assert(containingDeclaration is CallableMemberDescriptor) {
"containingDeclaration for descriptor have unsupported type for mangling, " +
"descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration
}
regularAndUnstable()
}
}
}
data class NameAndStability(val name: String, val stable: Boolean)
@JvmStatic fun getPrivateMangledName(baseName: String, descriptor: CallableDescriptor): String {
val ownerName = descriptor.containingDeclaration.fqNameUnsafe.asString()
return getStableMangledName(baseName, ownerName + ":" + getArgumentTypesAsString(descriptor))
@@ -214,67 +258,11 @@ class NameSuggestion {
return argTypes.toString()
}
// Sometimes private members of a class can clash with public members of subclasses, therefore we must
// mangle them
private fun shouldMangleUnstable(descriptor: DeclarationDescriptor): Boolean {
if (descriptor is ClassDescriptor) return false
if (DescriptorUtils.isDescriptorWithLocalVisibility(descriptor)) return false
val containingClass = DescriptorUtils.getContainingClass(descriptor)
if (containingClass != null && descriptor is CallableMemberDescriptor && !descriptor.isOverridable) {
return containingClass.visibility.isPublicAPI
}
return false
}
@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 (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
}
}
}
private fun mangledId(forCalculateId: String): String {
val absHashCode = Math.abs(forCalculateId.hashCode())
return if (absHashCode != 0) Integer.toString(absHashCode, Character.MAX_RADIX) else ""