KT-2752: refactor NameSuggestion, change rules for determining name stability and applying mangling
This commit is contained in:
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user