Merge :compiler:fir:jvm module into :compiler:fir:java
This commit is contained in:
committed by
TeamCityServer
parent
8ffe3764ef
commit
405670e111
@@ -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
|
||||
}
|
||||
}
|
||||
+104
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
+48
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user