JS: change how signature for mangling is generated, taking into account more information. See KT-15285
This commit is contained in:
@@ -608,7 +608,7 @@ public class KotlinTypeMapper {
|
||||
|
||||
mapType(argument.getType(), signatureVisitor,
|
||||
argumentMode.toGenericArgumentMode(
|
||||
TypeMappingUtil.getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind())));
|
||||
UtilsKt.getEffectiveVariance(parameter.getVariance(), argument.getProjectionKind())));
|
||||
|
||||
signatureVisitor.writeTypeArgumentEnd();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.jetbrains.kotlin.codegen.state
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES as BUILTIN_NAMES
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
@@ -30,6 +29,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.getEffectiveVariance
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES as BUILTIN_NAMES
|
||||
|
||||
fun KotlinType.isMostPreciseContravariantArgument(parameter: TypeParameterDescriptor): Boolean =
|
||||
// TODO: probably class upper bound should be used
|
||||
@@ -59,22 +60,6 @@ private fun KotlinType.canHaveSubtypesIgnoringNullability(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun getEffectiveVariance(parameterVariance: Variance, projectionKind: Variance): Variance {
|
||||
if (parameterVariance === Variance.INVARIANT) {
|
||||
return projectionKind
|
||||
}
|
||||
if (projectionKind === Variance.INVARIANT) {
|
||||
return parameterVariance
|
||||
}
|
||||
if (parameterVariance === projectionKind) {
|
||||
return parameterVariance
|
||||
}
|
||||
|
||||
// In<out X> = In<*>
|
||||
// Out<in X> = Out<*>
|
||||
return Variance.OUT_VARIANCE
|
||||
}
|
||||
|
||||
val CallableDescriptor?.isMethodWithDeclarationSiteWildcards: Boolean
|
||||
get() {
|
||||
if (this !is CallableMemberDescriptor) return false
|
||||
|
||||
@@ -36,4 +36,20 @@ internal fun hackForTypeIntersector(types: Collection<KotlinType>): KotlinType?
|
||||
ErrorTypesAreEqualToAnything.isSubtypeOf(candidate, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getEffectiveVariance(parameterVariance: Variance, projectionKind: Variance): Variance {
|
||||
if (parameterVariance === Variance.INVARIANT) {
|
||||
return projectionKind
|
||||
}
|
||||
if (projectionKind === Variance.INVARIANT) {
|
||||
return parameterVariance
|
||||
}
|
||||
if (parameterVariance === projectionKind) {
|
||||
return parameterVariance
|
||||
}
|
||||
|
||||
// In<out X> = In<*>
|
||||
// Out<in X> = Out<*>
|
||||
return Variance.OUT_VARIANCE
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.js.naming
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.getNameForAnnotatedObject
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -267,7 +266,7 @@ class NameSuggestion {
|
||||
return regularAndUnstable()
|
||||
}
|
||||
|
||||
fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, getArgumentTypesAsString(descriptor)), true)
|
||||
fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, encodeSignature(descriptor)), true)
|
||||
fun mangledPrivate() = NameAndStability(getPrivateMangledName(baseName, descriptor), false)
|
||||
|
||||
val effectiveVisibility = descriptor.ownEffectiveVisibility
|
||||
@@ -315,20 +314,7 @@ class NameSuggestion {
|
||||
|
||||
@JvmStatic fun getPrivateMangledName(baseName: String, descriptor: CallableDescriptor): String {
|
||||
val ownerName = descriptor.containingDeclaration.fqNameUnsafe.asString()
|
||||
return getStableMangledName(baseName, ownerName + ":" + getArgumentTypesAsString(descriptor))
|
||||
}
|
||||
|
||||
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()
|
||||
return getStableMangledName(baseName, ownerName + ":" + encodeSignature(descriptor))
|
||||
}
|
||||
|
||||
@JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.js.naming
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeProjection
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.getEffectiveVariance
|
||||
|
||||
fun encodeSignature(descriptor: CallableDescriptor): String {
|
||||
val sig = StringBuilder()
|
||||
|
||||
val typeParameterNames = nameTypeParameters(descriptor)
|
||||
val currentParameters = descriptor.typeParameters.filter { !it.isCapturedFromOuterDeclaration }.toSet()
|
||||
val usedTypeParameters = currentParameters.toMutableSet()
|
||||
val typeParameterNamer = { typeParameter: TypeParameterDescriptor ->
|
||||
usedTypeParameters += typeParameter.original
|
||||
typeParameterNames[typeParameter.original]!!
|
||||
}
|
||||
|
||||
val receiverParameter = descriptor.extensionReceiverParameter
|
||||
if (receiverParameter != null) {
|
||||
sig.encodeForSignature(receiverParameter.type, typeParameterNamer).append('/')
|
||||
}
|
||||
|
||||
for (valueParameter in descriptor.valueParameters) {
|
||||
if (valueParameter.index > 0) {
|
||||
sig.append(",")
|
||||
}
|
||||
if (valueParameter.varargElementType != null) {
|
||||
sig.append("*")
|
||||
}
|
||||
sig.encodeForSignature(valueParameter.type, typeParameterNamer)
|
||||
}
|
||||
|
||||
var first = true
|
||||
for (typeParameter in typeParameterNames.keys.asSequence().filter { it in usedTypeParameters }) {
|
||||
val upperBounds = typeParameter.upperBounds.filter { !KotlinBuiltIns.isNullableAny(it) }
|
||||
if (upperBounds.isEmpty() && typeParameter !in currentParameters) continue
|
||||
|
||||
sig.append(if (first) "|" else ",").append(typeParameterNames[typeParameter])
|
||||
first = false
|
||||
if (upperBounds.isEmpty()) continue
|
||||
|
||||
sig.append("<:")
|
||||
for ((boundIndex, upperBound) in upperBounds.withIndex()) {
|
||||
if (boundIndex > 0) {
|
||||
sig.append("&")
|
||||
}
|
||||
sig.encodeForSignature(upperBound, typeParameterNamer)
|
||||
}
|
||||
}
|
||||
|
||||
return sig.toString()
|
||||
}
|
||||
|
||||
private fun StringBuilder.encodeForSignature(
|
||||
type: KotlinType,
|
||||
typeParameterNamer: (TypeParameterDescriptor) -> String
|
||||
): StringBuilder {
|
||||
val declaration = type.constructor.declarationDescriptor!!
|
||||
if (declaration is TypeParameterDescriptor) {
|
||||
return append(typeParameterNamer(declaration))
|
||||
}
|
||||
|
||||
append(DescriptorUtils.getFqName(declaration).asString())
|
||||
|
||||
if (type.arguments.isNotEmpty()) {
|
||||
val parameters = declaration.typeConstructor.parameters
|
||||
append("<")
|
||||
for ((index, argument) in type.arguments.withIndex()) {
|
||||
if (index > 0) {
|
||||
append(",")
|
||||
}
|
||||
encodeForSignature(argument, parameters[index], typeParameterNamer)
|
||||
}
|
||||
append(">")
|
||||
}
|
||||
|
||||
if (type.isMarkedNullable) {
|
||||
append("?")
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun StringBuilder.encodeForSignature(
|
||||
projection: TypeProjection,
|
||||
parameter: TypeParameterDescriptor,
|
||||
typeParameterNamer: (TypeParameterDescriptor) -> String
|
||||
): StringBuilder {
|
||||
if (projection.isStarProjection) {
|
||||
return append("*")
|
||||
}
|
||||
else {
|
||||
when (getEffectiveVariance(parameter.variance, projection.projectionKind)) {
|
||||
Variance.IN_VARIANCE -> append("-")
|
||||
Variance.OUT_VARIANCE -> append("+")
|
||||
Variance.INVARIANT -> {}
|
||||
}
|
||||
return encodeForSignature(projection.type, typeParameterNamer)
|
||||
}
|
||||
}
|
||||
|
||||
private fun nameTypeParameters(descriptor: DeclarationDescriptor): Map<TypeParameterDescriptor, String> {
|
||||
val result = mutableMapOf<TypeParameterDescriptor, String>()
|
||||
for ((listIndex, list) in collectTypeParameters(descriptor).withIndex()) {
|
||||
for ((indexInList, typeParameter) in list.withIndex()) {
|
||||
result[typeParameter] = "$listIndex:$indexInList"
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun collectTypeParameters(descriptor: DeclarationDescriptor): List<List<TypeParameterDescriptor>> {
|
||||
var currentDescriptor: DeclarationDescriptor? = descriptor
|
||||
val result = mutableListOf<List<TypeParameterDescriptor>>()
|
||||
while (currentDescriptor != null) {
|
||||
getOwnTypeParameters(currentDescriptor)?.let { result += it }
|
||||
currentDescriptor = if (currentDescriptor is ConstructorDescriptor) {
|
||||
currentDescriptor.constructedClass.containingDeclaration
|
||||
}
|
||||
else {
|
||||
currentDescriptor.containingDeclaration
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getOwnTypeParameters(descriptor: DeclarationDescriptor): List<TypeParameterDescriptor>? =
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> descriptor.declaredTypeParameters.filter { !it.isCapturedFromOuterDeclaration }
|
||||
is PropertyAccessorDescriptor -> getOwnTypeParameters(descriptor.correspondingProperty)
|
||||
is CallableDescriptor -> descriptor.typeParameters.filter { !it.isCapturedFromOuterDeclaration }
|
||||
else -> null
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.js.test
|
||||
|
||||
import com.intellij.mock.MockVirtualFileSystem
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.js.config.LibrarySourcesConfig
|
||||
import org.jetbrains.kotlin.js.naming.encodeSignature
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class EncodeSignatureTest {
|
||||
@Test
|
||||
fun function() {
|
||||
assertSignature("", "fun test() {}")
|
||||
assertSignature("kotlin.Int", "fun test(x: Int) {}")
|
||||
assertSignature("kotlin.Int,kotlin.Long", "fun test(x: Int, y: Long) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extensionFunction() {
|
||||
assertSignature("kotlin.Int/", "fun Int.test() {}")
|
||||
assertSignature("kotlin.Int/kotlin.Long", "fun Int.test(x: Long) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun property() {
|
||||
assertSignature("", "val test: Int = 23")
|
||||
assertSignature("", "class A(val test: Int)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun extensionProperty() {
|
||||
assertSignature("kotlin.String/", "val String.test: Int = 23")
|
||||
assertSignature("kotlin.String/", "val String.test: Int = 23", "<get-test>")
|
||||
assertSignature("kotlin.String/kotlin.Int", "var String.test: Int = 23", "<set-test>")
|
||||
|
||||
assertSignature("kotlin.String/", "class A { val String.test: Int get() = 23 }")
|
||||
assertSignature("kotlin.String/", "class A { val String.test: Int get() = 23 }", "<get-test>")
|
||||
assertSignature("kotlin.String/kotlin.Int", "class A { var String.test: Int get() = 23; set(_) {} }", "<set-test>")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genericType() {
|
||||
assertSignature("kotlin.Array<kotlin.String>", "fun test(x: Array<String>) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun varargParam() {
|
||||
assertSignature("*kotlin.Array<+kotlin.String>", "fun test(vararg x: String) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nullableType() {
|
||||
assertSignature("kotlin.String?", "fun test(x: String?) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ownTypeParameters() {
|
||||
assertSignature("0:0,0:1|0:0,0:1", "fun <S, T> test(x: S, y: T) {}")
|
||||
assertSignature("0:1,0:0|0:0,0:1", "fun <S, T> test(x: T, y: S) {}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun enclosingTypeParameters() {
|
||||
assertSignature("2:0,1:0,0:0|0:0", """
|
||||
class A<S> {
|
||||
inner class B<T> {
|
||||
fun <U> test(x: S, y: T, z: U) {}
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variance() {
|
||||
assertSignature("A<kotlin.Int>,A<-kotlin.Long>,A<+kotlin.String>", """
|
||||
class A<T>
|
||||
fun test(x: A<Int>, y: A<in Long>, z: A<out String>)
|
||||
""")
|
||||
|
||||
assertSignature("A<-kotlin.Int>,A<-kotlin.Long>,A<+kotlin.String>", """
|
||||
class A<in T>
|
||||
fun test(x: A<Int>, y: A<in Long>, z: A<out String>)
|
||||
""")
|
||||
|
||||
assertSignature("A<+kotlin.Int>,A<+kotlin.Long>,A<+kotlin.String>", """
|
||||
class A<out T>
|
||||
fun test(x: A<Int>, y: A<in Long>, z: A<out String>)
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun starProjection() {
|
||||
assertSignature("A<*>", """
|
||||
class A<T>
|
||||
fun test(x: A<*>)
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typeConstraints() {
|
||||
assertSignature("0:0|0:0<:A<kotlin.Int>", """
|
||||
class A<T>
|
||||
fun <T : A<Int>> test(x: T)
|
||||
""")
|
||||
|
||||
assertSignature("0:0|0:0<:A<kotlin.Int>&A<kotlin.String>", """
|
||||
class A<T>
|
||||
fun <T> test(x: T) where T : A<Int>, T : A<String>
|
||||
""")
|
||||
|
||||
assertSignature("1:0,0:0|0:0<:1:0", """
|
||||
class A<T> {
|
||||
fun <S> test(x: T, x: S) where S : T
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun genericExtensionFunctionToInner() {
|
||||
assertSignature("A.C<kotlin.String,2:0>/1:0", """
|
||||
class A<T> {
|
||||
inner class B<S> {
|
||||
fun C<String>.test(x: S) {}
|
||||
}
|
||||
inner class C<U>
|
||||
}
|
||||
fun <T : A<Int>> test(x: T)
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typeAliases() {
|
||||
assertSignature("C<kotlin.String>", """
|
||||
class C<T>
|
||||
typealias A = C<String>
|
||||
fun test(x: A)
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun typeParameterUsageInConstraints() {
|
||||
assertSignature("0:0|0:0<:1:0,1:0<:I", """
|
||||
interface I
|
||||
class A<T : I> {
|
||||
fun <S : T> test(x: S)
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun recursiveType() {
|
||||
assertSignature("1:0|1:0<:I<1:0>", """
|
||||
interface I<T : I<T>> {
|
||||
fun test(x: T)
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
private fun assertSignature(expectedEncoding: String, codeSnippet: String, name: String = "test") {
|
||||
val environment = createEnvironment()
|
||||
val project = environment.project
|
||||
|
||||
val fs = MockVirtualFileSystem()
|
||||
val file = fs.file("sample.kt", codeSnippet).findFileByPath("/sample.kt")
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
val psiFile = psiManager.findFile(file) as KtFile
|
||||
|
||||
val configuration = environment.configuration.copy()
|
||||
configuration.put(JSConfigurationKeys.LIBRARY_FILES, LibrarySourcesConfig.JS_STDLIB)
|
||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, "sample")
|
||||
|
||||
val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles(listOf(psiFile), LibrarySourcesConfig(project, configuration))
|
||||
val module = analysisResult.moduleDescriptor
|
||||
val rootPackage = module.getPackage(FqName.ROOT)
|
||||
|
||||
val testDescriptor = findTestCallable(rootPackage, name) ?:
|
||||
error("Descriptor named `$name` was not found in provided snippet: $codeSnippet")
|
||||
val actualEncoding = encodeSignature(testDescriptor)
|
||||
Assert.assertEquals(expectedEncoding, actualEncoding)
|
||||
}
|
||||
|
||||
private fun findTestCallable(scope: MemberScope, name: String): CallableMemberDescriptor? {
|
||||
return scope.getContributedDescriptors().asSequence().mapNotNull { findTestCallable(it, name) }.firstOrNull()
|
||||
}
|
||||
|
||||
private fun findTestCallable(descriptor: DeclarationDescriptor, name: String): CallableMemberDescriptor? {
|
||||
return when (descriptor) {
|
||||
is PackageViewDescriptor -> findTestCallable(descriptor.memberScope, name)
|
||||
is ClassDescriptor -> findTestCallable(descriptor.unsubstitutedMemberScope, name)
|
||||
is PropertyDescriptor -> {
|
||||
if (descriptor.name.asString() == name) {
|
||||
descriptor
|
||||
}
|
||||
else {
|
||||
descriptor.accessors.asSequence().mapNotNull { findTestCallable(it, name) }.firstOrNull()
|
||||
}
|
||||
}
|
||||
is CallableMemberDescriptor -> if (descriptor.name.asString() == name) descriptor else null
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun createEnvironment(): KotlinCoreEnvironment {
|
||||
return KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user