Merge :compiler:fir:jvm module into :compiler:fir:java

This commit is contained in:
Dmitriy Novozhilov
2021-09-08 15:59:27 +03:00
committed by TeamCityServer
parent 8ffe3764ef
commit 405670e111
18 changed files with 2 additions and 26 deletions
+1 -1
View File
@@ -7,11 +7,11 @@ plugins {
dependencies {
api(project(":core:compiler.common.jvm"))
api(project(":core:metadata.jvm"))
api(project(":compiler:config.jvm"))
api(project(":compiler:resolution.common.jvm"))
api(project(":compiler:frontend.common"))
api(project(":compiler:fir:resolve"))
api(project(":compiler:fir:checkers"))
api(project(":compiler:fir:jvm"))
api(project(":compiler:fir:fir-deserialization"))
implementation(project(":core:deserialization.common.jvm"))
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
@NoMutableState
class FirJavaClassMapper(private val session: FirSession) : FirPlatformClassMapper() {
override fun getCorrespondingPlatformClass(declaration: FirClassLikeDeclaration): FirRegularClass? {
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(declaration.symbol.classId.asSingleFqName().toUnsafe())
return javaClassId?.let { session.symbolProvider.getClassLikeSymbolByFqName(it)?.fir } as? FirRegularClass
}
}
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticNamesProvider
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
@NoMutableState
object FirJavaSyntheticNamesProvider : FirSyntheticNamesProvider() {
private const val GETTER_PREFIX = "get"
private const val SETTER_PREFIX = "set"
private const val IS_PREFIX = "is"
@Suppress("NOTHING_TO_INLINE")
private inline fun Char.isAsciiUpperCase() = this in 'A'..'Z'
@Suppress("NOTHING_TO_INLINE")
private inline fun Char.isAsciiLowerCase() = this in 'a'..'z'
override fun possibleGetterNamesByPropertyName(name: Name): List<Name> {
if (name.isSpecial) return emptyList()
val identifier = name.identifier
if (identifier.isEmpty()) return emptyList()
val firstChar = identifier[0]
if (!firstChar.isJavaIdentifierStart() || firstChar in 'A'..'Z') return emptyList()
val result = ArrayList<Name>(3)
val standardName = Name.identifier(GETTER_PREFIX + identifier.capitalizeAsciiOnly())
val length = identifier.length
if (length == 1) {
if (identifier[0].isAsciiLowerCase()) {
// 'x' --> 'getX' but not 'X' --> 'getX'
result += standardName
}
} else if (identifier[1].isAsciiLowerCase()) {
if (identifier[0].isAsciiLowerCase()) {
// 'something' --> 'getSomething' classic case
result += standardName
}
var secondWordStart = 2
while (secondWordStart < length && identifier[secondWordStart].isAsciiLowerCase()) {
secondWordStart++
}
val capitalizedFirstWordName = Name.identifier(
GETTER_PREFIX + identifier.substring(0, secondWordStart).toUpperCaseAsciiOnly() + identifier.substring(secondWordStart)
)
if (secondWordStart >= length || identifier[secondWordStart].isAsciiUpperCase()) {
// 'xyz' --> 'getXYZ' or 'xyzOfSomething' --> 'getXYZOfSomething'
result += capitalizedFirstWordName
}
} else if (length < 3 || !identifier[2].isAsciiUpperCase()) {
// 'xOfSomething' --> 'getXOfSomething' but not 'xYZ' --> 'getXYZ'
result += standardName
}
if (length > IS_PREFIX.length && identifier.startsWith(IS_PREFIX) && !identifier[IS_PREFIX.length].isAsciiLowerCase()) {
// 'isSomething' (but not 'is' or 'issomething')
result += name
}
return result
}
override fun setterNameByGetterName(name: Name): Name? {
val identifier = name.identifier
val prefix = when {
identifier.startsWith(GETTER_PREFIX) -> GETTER_PREFIX
identifier.startsWith(IS_PREFIX) -> IS_PREFIX
else -> return null
}
return Name.identifier(SETTER_PREFIX + identifier.removePrefix(prefix))
}
override fun getterNameBySetterName(name: Name): Name? {
val identifier = name.identifier
val prefix = when {
identifier.startsWith(SETTER_PREFIX) -> SETTER_PREFIX
else -> return null
}
return Name.identifier(GETTER_PREFIX + identifier.removePrefix(prefix))
}
override fun possiblePropertyNamesByAccessorName(name: Name): List<Name> {
if (name.isSpecial) return emptyList()
val identifier = name.identifier
val prefix = when {
identifier.startsWith(GETTER_PREFIX) -> GETTER_PREFIX
identifier.startsWith(IS_PREFIX) -> ""
identifier.startsWith(SETTER_PREFIX) -> SETTER_PREFIX
else -> return emptyList()
}
val withoutPrefix = identifier.removePrefix(prefix)
val withoutPrefixName = Name.identifier(withoutPrefix.decapitalizeAsciiOnly())
return if (prefix == SETTER_PREFIX) {
listOf(withoutPrefixName, Name.identifier(IS_PREFIX + withoutPrefix))
} else {
listOf(withoutPrefixName)
}
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.calls.jvm
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.resolve.calls.AbstractConeCallConflictResolver
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
// This conflict resolver filters JVM equivalent top-level functions
// like emptyArray() from intrinsics and built-ins
class ConeEquivalentCallConflictResolver(
specificityComparator: TypeSpecificityComparator,
inferenceComponents: InferenceComponents
) : AbstractConeCallConflictResolver(specificityComparator, inferenceComponents) {
override fun chooseMaximallySpecificCandidates(
candidates: Set<Candidate>,
discriminateGenerics: Boolean,
discriminateAbstracts: Boolean
): Set<Candidate> {
return filterOutEquivalentCalls(candidates)
}
private fun filterOutEquivalentCalls(candidates: Collection<Candidate>): Set<Candidate> {
val result = mutableSetOf<Candidate>()
outerLoop@ for (myCandidate in candidates) {
val me = myCandidate.symbol.fir
if (me is FirCallableDeclaration && me.symbol.containingClass() == null) {
for (otherCandidate in result) {
val other = otherCandidate.symbol.fir
if (other is FirCallableDeclaration && other.symbol.containingClass() == null) {
if (areEquivalentTopLevelCallables(me, myCandidate, other, otherCandidate)) {
continue@outerLoop
}
}
}
}
result += myCandidate
}
return result
}
private fun areEquivalentTopLevelCallables(
first: FirCallableDeclaration,
firstCandidate: Candidate,
second: FirCallableDeclaration,
secondCandidate: Candidate
): Boolean {
if (first.symbol.callableId != second.symbol.callableId) return false
if (first.isExpect != second.isExpect) return false
if (first.receiverTypeRef?.coneType != second.receiverTypeRef?.coneType) {
return false
}
val firstSignature = createFlatSignature(firstCandidate, first)
val secondSignature = createFlatSignature(secondCandidate, second)
return compareCallsByUsedArguments(firstSignature, secondSignature, false) &&
compareCallsByUsedArguments(secondSignature, firstSignature, false)
}
private fun createFlatSignature(call: Candidate, declaration: FirCallableDeclaration): FlatSignature<Candidate> {
return when (declaration) {
is FirSimpleFunction -> createFlatSignature(call, declaration)
is FirConstructor -> createFlatSignature(call, declaration)
is FirVariable -> createFlatSignature(call, declaration)
else -> error("Not supported: $declaration")
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.calls.jvm
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.resolve.calls.ConeCallConflictResolverFactory
import org.jetbrains.kotlin.fir.resolve.calls.ConeCompositeConflictResolver
import org.jetbrains.kotlin.fir.resolve.calls.ConeOverloadConflictResolver
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
import org.jetbrains.kotlin.resolve.jvm.JvmTypeSpecificityComparator
@NoMutableState
object JvmCallConflictResolverFactory : ConeCallConflictResolverFactory() {
override fun create(
typeSpecificityComparator: TypeSpecificityComparator,
components: InferenceComponents
): ConeCompositeConflictResolver {
val specificityComparator = JvmTypeSpecificityComparator(components.ctx)
return ConeCompositeConflictResolver(
ConeOverloadConflictResolver(specificityComparator, components),
ConeEquivalentCallConflictResolver(specificityComparator, components),
JvmPlatformOverloadsConflictResolver(specificityComparator, components)
)
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.calls.jvm
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirField
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.calls.AbstractConeCallConflictResolver
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
import org.jetbrains.kotlin.fir.resolve.inference.InferenceComponents
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
class JvmPlatformOverloadsConflictResolver(
specificityComparator: TypeSpecificityComparator,
inferenceComponents: InferenceComponents
) : AbstractConeCallConflictResolver(specificityComparator, inferenceComponents) {
override fun chooseMaximallySpecificCandidates(
candidates: Set<Candidate>,
discriminateGenerics: Boolean,
discriminateAbstracts: Boolean
): Set<Candidate> {
if (!inferenceComponents.session.languageVersionSettings.supportsFeature(LanguageFeature.PreferJavaFieldOverload)) {
return candidates
}
val result = mutableSetOf<Candidate>()
outerLoop@ for (myCandidate in candidates) {
val me = myCandidate.symbol.fir
if (me is FirProperty && me.symbol.containingClass() != null) {
for (otherCandidate in candidates) {
val other = otherCandidate.symbol.fir
if (other is FirField && other.symbol.containingClass() != null) {
// NB: FE 1.0 does class equivalence check here
// However, in FIR container classes aren't the same for our samples (see fieldPropertyOverloads.kt)
// E.g. we can have SomeConcreteJavaEnum for field and kotlin.Enum for static property 'name'
continue@outerLoop
}
}
}
result += myCandidate
}
return result
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.scopes
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.impl.FirClassSubstitutionScope
import org.jetbrains.kotlin.fir.scopes.jvm.JvmMappedScope
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
fun wrapScopeWithJvmMapped(
klass: FirClass,
declaredMemberScope: FirScope,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirScope {
val classId = klass.classId
val kotlinUnsafeFqName = classId.asSingleFqName().toUnsafe()
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(kotlinUnsafeFqName)
?: return declaredMemberScope
val symbolProvider = useSiteSession.symbolProvider
val javaClass = symbolProvider.getClassLikeSymbolByFqName(javaClassId)?.fir as? FirRegularClass
?: return declaredMemberScope
val preparedSignatures = JvmMappedScope.prepareSignatures(javaClass, JavaToKotlinClassMap.isMutable(kotlinUnsafeFqName))
return if (preparedSignatures.isNotEmpty()) {
javaClass.unsubstitutedScope(useSiteSession, scopeSession, withForcedTypeCalculator = false).let { javaClassUseSiteScope ->
val jvmMappedScope = JvmMappedScope(
useSiteSession,
klass,
javaClass,
declaredMemberScope,
javaClassUseSiteScope,
preparedSignatures
)
if (klass !is FirRegularClass) {
jvmMappedScope
} else {
// We should substitute Java type parameters with base Kotlin type parameters to match overrides properly
// It's necessary for MutableMap, which has *two* JavaMappedScope inside (one for itself and another for base Map)
wrapSubstitutionScopeIfNeed(
useSiteSession, jvmMappedScope, klass, scopeSession,
derivedClass = klass,
)
}
}
} else {
declaredMemberScope
}
}
private fun wrapSubstitutionScopeIfNeed(
session: FirSession,
useSiteMemberScope: FirTypeScope,
declaration: FirClass,
builder: ScopeSession,
derivedClass: FirRegularClass
): FirTypeScope {
if (declaration.typeParameters.isEmpty()) return useSiteMemberScope
return builder.getOrBuild(declaration.symbol, PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY) {
val platformClass = session.platformClassMapper.getCorrespondingPlatformClass(declaration) ?: return@getOrBuild useSiteMemberScope
// This kind of substitution is necessary when method which is mapped from Java (e.g. Java Map.forEach)
// is called on an external type, like MyMap<String, String>,
// to determine parameter types properly (e.g. String, String instead of K, V)
val platformTypeParameters = platformClass.typeParameters
val platformSubstitution = createSubstitution(platformTypeParameters, declaration.defaultType(), session)
val substitutor = substitutorByMap(platformSubstitution, session)
FirClassSubstitutionScope(
session, useSiteMemberScope, PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY, substitutor,
dispatchReceiverTypeForSubstitutedMembers = derivedClass.defaultType(),
skipPrivateMembers = true,
)
}
}
private val PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY = scopeSessionKey<FirClassSymbol<*>, FirTypeScope>()
@@ -0,0 +1,143 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.scopes.jvm
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
import org.jetbrains.kotlin.fir.types.jvm.FirJavaTypeRef
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.StandardClassIds
fun FirFunction.computeJvmSignature(typeConversion: (FirTypeRef) -> ConeKotlinType? = FirTypeRef::coneTypeSafe): String? {
val containingClass = containingClass() ?: return null
return SignatureBuildingComponents.signature(containingClass.classId, computeJvmDescriptor(typeConversion = typeConversion))
}
// TODO: `typeConversion` is only used for converting Java types into cone types, but shouldn't it be trivial
// to construct a JVM descriptor from a Java type directly? The question is how to make the two paths consistent...
fun FirFunction.computeJvmDescriptor(
customName: String? = null,
includeReturnType: Boolean = true,
typeConversion: (FirTypeRef) -> ConeKotlinType? = FirTypeRef::coneTypeSafe
): String = buildString {
if (customName != null) {
append(customName)
} else {
if (this@computeJvmDescriptor is FirSimpleFunction) {
append(name.asString())
} else {
append("<init>")
}
}
append("(")
for (parameter in valueParameters) {
typeConversion(parameter.returnTypeRef)?.let { appendConeType(it, typeConversion) }
}
append(")")
if (includeReturnType) {
if (this@computeJvmDescriptor !is FirSimpleFunction || returnTypeRef.isVoid()) {
append("V")
} else {
typeConversion(returnTypeRef)?.let { appendConeType(it, typeConversion) }
}
}
}
private val PRIMITIVE_TYPE_SIGNATURE: Map<String, String> = mapOf(
"Boolean" to "Z",
"Byte" to "B",
"Char" to "C",
"Short" to "S",
"Int" to "I",
"Long" to "J",
"Float" to "F",
"Double" to "D",
)
private fun StringBuilder.appendConeType(coneType: ConeKotlinType, typeConversion: (FirTypeRef) -> ConeKotlinType?) {
(coneType as? ConeClassLikeType)?.let {
val classId = it.lookupTag.classId
if (classId.packageFqName.toString() == "kotlin") {
PRIMITIVE_TYPE_SIGNATURE[classId.shortClassName.identifier]?.let { signature ->
append(signature)
return
}
}
}
fun appendClassLikeType(type: ConeClassLikeType) {
val baseClassId = type.lookupTag.classId
// TODO: what about primitive arrays?
val classId = JavaToKotlinClassMap.mapKotlinToJava(baseClassId.asSingleFqName().toUnsafe()) ?: baseClassId
if (classId == StandardClassIds.Array) {
append("[")
type.typeArguments.forEach { typeArg ->
when (typeArg) {
ConeStarProjection -> append("*")
is ConeKotlinTypeProjection -> appendConeType(typeArg.type, typeConversion)
}
}
} else {
append("L")
append(classId.packageFqName.asString().replace(".", "/"))
append("/")
append(classId.relativeClassName)
append(";")
}
}
when (coneType) {
is ConeClassErrorType -> Unit // TODO: just skipping it seems wrong
is ConeClassLikeType -> {
appendClassLikeType(coneType)
}
is ConeTypeParameterType -> {
// TODO: 1. unannotated bounds are probably flexible, so this isn't right;
// 2. shouldn't this always take the first bound and recurse if it's also a type parameter?
coneType.lookupTag.typeParameterSymbol.fir.bounds.firstNotNullOfOrNull {
val converted = typeConversion(it)
if (converted is ConeClassLikeType) it to converted else null
}?.let { (firBound, coneBound) ->
// TODO: pretty sure Java type conversion does not produce either of these
if (firBound !is FirImplicitNullableAnyTypeRef && firBound !is FirImplicitAnyTypeRef) {
appendClassLikeType(coneBound)
return
}
}
append("Ljava/lang/Object;")
}
is ConeDefinitelyNotNullType -> appendConeType(coneType.original, typeConversion)
is ConeFlexibleType -> appendConeType(coneType.lowerBound, typeConversion)
else -> Unit // TODO: throw an error? should check that Java type conversion/enhancement can only produce these cone types
}
}
private val unitClassId = ClassId.topLevel(FqName("kotlin.Unit"))
private fun FirTypeRef.isVoid(): Boolean {
return when (this) {
is FirJavaTypeRef -> {
val type = type
type is JavaPrimitiveType && type.type == null
}
is FirResolvedTypeRef -> {
val type = type
type is ConeClassLikeType && type.lookupTag.classId == unitClassId
}
else -> false
}
}
@@ -0,0 +1,179 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.scopes.jvm
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltInsSignatures
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.FirFakeOverrideGenerator
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
class JvmMappedScope(
private val session: FirSession,
private val firKotlinClass: FirClass,
private val firJavaClass: FirRegularClass,
private val declaredMemberScope: FirScope,
private val javaMappedClassUseSiteScope: FirTypeScope,
private val signatures: Signatures
) : FirTypeScope() {
private val functionsCache = mutableMapOf<FirNamedFunctionSymbol, FirNamedFunctionSymbol>()
private val substitutor = ConeSubstitutorByMap(
firJavaClass.typeParameters.zip(firKotlinClass.typeParameters).map { (javaParameter, kotlinParameter) ->
javaParameter.symbol to ConeTypeParameterTypeImpl(ConeTypeParameterLookupTag(kotlinParameter.symbol), isNullable = false)
}.toMap(),
session
)
private val kotlinDispatchReceiverType = firKotlinClass.defaultType()
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
val visibleMethods = signatures.visibleMethodSignaturesByName[name]
?: return declaredMemberScope.processFunctionsByName(name, processor)
val declared = mutableListOf<FirNamedFunctionSymbol>()
declaredMemberScope.processFunctionsByName(name) { symbol ->
declared += symbol
processor(symbol)
}
val declaredSignatures by lazy {
declared.mapTo(mutableSetOf()) { it.fir.computeJvmDescriptor() }
}
javaMappedClassUseSiteScope.processFunctionsByName(name) { symbol ->
val newSymbol = getOrCreateSubstitutedCopy(symbol)
val jvmSignature = newSymbol.fir.computeJvmDescriptor()
if (jvmSignature in visibleMethods && jvmSignature !in declaredSignatures) {
processor(newSymbol)
}
}
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
declaredMemberScope.processPropertiesByName(name, processor)
}
private fun getOrCreateSubstitutedCopy(symbol: FirNamedFunctionSymbol): FirNamedFunctionSymbol {
return functionsCache.getOrPut(symbol) {
val oldFunction = symbol.fir
val newSymbol = FirNamedFunctionSymbol(CallableId(firKotlinClass.classId, symbol.callableId.callableName))
FirFakeOverrideGenerator.createCopyForFirFunction(
newSymbol,
baseFunction = symbol.fir,
session,
symbol.fir.origin,
newDispatchReceiverType = kotlinDispatchReceiverType,
newParameterTypes = oldFunction.valueParameters.map { substitutor.substituteOrSelf(it.returnTypeRef.coneType) },
newReturnType = substitutor.substituteOrSelf(oldFunction.returnTypeRef.coneType)
)
newSymbol
}
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
) = ProcessorAction.NONE
override fun processDeclaredConstructors(processor: (FirConstructorSymbol) -> Unit) {
val hiddenConstructors = signatures.hiddenConstructors
if (hiddenConstructors.isNotEmpty()) {
javaMappedClassUseSiteScope.processDeclaredConstructors { symbol ->
val jvmSignature = symbol.fir.computeJvmDescriptor()
if (jvmSignature !in hiddenConstructors) {
processor(symbol)
}
}
} else {
javaMappedClassUseSiteScope.processDeclaredConstructors(processor)
}
declaredMemberScope.processDeclaredConstructors(processor)
}
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction = ProcessorAction.NONE
override fun processClassifiersByNameWithSubstitution(name: Name, processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit) {
declaredMemberScope.processClassifiersByNameWithSubstitution(name, processor)
}
override fun getCallableNames(): Set<Name> {
return declaredMemberScope.getContainingCallableNamesIfPresent() + signatures.visibleMethodSignaturesByName.keys
}
override fun getClassifierNames(): Set<Name> {
return declaredMemberScope.getContainingClassifierNamesIfPresent()
}
companion object {
data class Signatures(val visibleMethodSignaturesByName: Map<Name, Set<String>>, val hiddenConstructors: Set<String>) {
fun isEmpty() = visibleMethodSignaturesByName.isEmpty() && hiddenConstructors.isEmpty()
fun isNotEmpty() = !isEmpty()
}
// NOTE: No-arg constructors
@OptIn(ExperimentalStdlibApi::class)
private val additionalHiddenConstructors = buildSet<String> {
// kotlin.text.String pseudo-constructors should be used instead of java.lang.String constructors
listOf(
"",
"Lkotlin/ByteArray;IILjava/nio/charset/Charset;",
"Lkotlin/ByteArray;Ljava/nio/charset/Charset;",
"Lkotlin/ByteArray;II",
"Lkotlin/ByteArray;",
"Lkotlin/CharArray;",
"Lkotlin/CharArray;II",
"Lkotlin/IntArray;II",
"Ljava/lang/StringBuffer;",
"Ljava/lang/StringBuilder;",
).mapTo(this) { arguments -> "java/lang/String.<init>($arguments)V" }
listOf(
"",
"Ljava/lang/String;Ljava/lang/Throwable;",
"Ljava/lang/Throwable;",
"Ljava/lang/String;"
).mapTo(this) { arguments -> "java/lang/Throwable.<init>($arguments)V" }
}
fun prepareSignatures(klass: FirRegularClass, isMutable: Boolean): Signatures {
val signaturePrefix = klass.symbol.classId.toString()
val visibleMethodsByName = mutableMapOf<Name, MutableSet<String>>()
JvmBuiltInsSignatures.VISIBLE_METHOD_SIGNATURES.filter { signature ->
signature in JvmBuiltInsSignatures.MUTABLE_METHOD_SIGNATURES == isMutable &&
signature.startsWith(signaturePrefix)
}.map { signature ->
// +1 to delete dot before function name
signature.substring(signaturePrefix.length + 1)
}.forEach {
visibleMethodsByName.getOrPut(Name.identifier(it.substringBefore("("))) { mutableSetOf() }.add(it)
}
val hiddenConstructors =
(JvmBuiltInsSignatures.HIDDEN_CONSTRUCTOR_SIGNATURES + additionalHiddenConstructors)
.filter { it.startsWith(signaturePrefix) }
.mapTo(mutableSetOf()) { it.substring(signaturePrefix.length + 1) }
return Signatures(visibleMethodsByName, hiddenConstructors)
}
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.types.jvm
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.builder.FirBuilderDsl
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.types.FirQualifierPart
import org.jetbrains.kotlin.fir.types.FirTypeProjection
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.fir.visitors.transformInplace
import org.jetbrains.kotlin.load.java.structure.JavaArrayType
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
import org.jetbrains.kotlin.load.java.structure.JavaPrimitiveType
import org.jetbrains.kotlin.load.java.structure.JavaType
class FirJavaTypeRef(
val type: JavaType,
annotationBuilder: () -> List<FirAnnotationCall>,
override val qualifier: MutableList<FirQualifierPart>
) : FirUserTypeRef(), FirAnnotationContainer {
override val customRenderer: Boolean
get() = true
override val isMarkedNullable: Boolean
get() = false
override val source: FirSourceElement?
get() = null
override val annotations: List<FirAnnotationCall> by lazy { annotationBuilder() }
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
for (part in qualifier) {
part.typeArgumentList.typeArguments.forEach { it.accept(visitor, data) }
}
annotations.forEach { it.accept(visitor, data) }
}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirUserTypeRef {
for (part in qualifier) {
(part.typeArgumentList.typeArguments as MutableList<FirTypeProjection>).transformInplace(transformer, data)
}
return this
}
override fun <D> transformAnnotations(transformer: FirTransformer<D>, data: D): FirUserTypeRef {
return this
}
override fun toString(): String {
return type.render()
}
}
@FirBuilderDsl
class FirJavaTypeRefBuilder {
lateinit var annotationBuilder: () -> List<FirAnnotationCall>
lateinit var type: JavaType
val qualifier: MutableList<FirQualifierPart> = mutableListOf()
fun build(): FirJavaTypeRef {
return FirJavaTypeRef(type, annotationBuilder, qualifier)
}
}
inline fun buildJavaTypeRef(init: FirJavaTypeRefBuilder.() -> Unit): FirJavaTypeRef {
return FirJavaTypeRefBuilder().apply(init).build()
}
private fun JavaType?.render(): String {
return when (this) {
is JavaArrayType -> "${componentType.render()}[]"
is JavaClassifierType -> if (typeArguments.isEmpty()) {
classifierQualifiedName
} else {
classifierQualifiedName + typeArguments.joinToString(separator = ", ", prefix = "<", postfix = ">") { it.render() }
}
is JavaPrimitiveType -> type?.typeName?.identifier ?: "void"
else -> toString()
}
}