[LL API] Support '_DebugLabel's in code fragments

This commit is contained in:
Yan Zhulanow
2023-07-07 22:22:01 +09:00
committed by Space Team
parent af51b41a8a
commit 8a5cab6831
5 changed files with 139 additions and 5 deletions
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2023 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.analysis.providers
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.kotlin.psi.KtCodeFragment
public interface ForeignValueProviderService {
public fun getForeignValues(codeFragment: KtCodeFragment): Map<String, String>
public companion object {
public fun getInstance(): ForeignValueProviderService? {
return ApplicationManager.getApplication().getService(ForeignValueProviderService::class.java)
}
}
}
@@ -180,6 +180,7 @@ private class CodeFragmentCapturedValueVisitor(
if (symbol.isLocal) {
val isCrossingInlineBounds = isCrossingInlineBounds(element, symbol)
val capturedValue = when {
symbol.fir.foreignValueMarker == true -> CodeFragmentCapturedValue.ForeignValue(symbol.name, isCrossingInlineBounds)
symbol.hasDelegate -> CodeFragmentCapturedValue.LocalDelegate(symbol.name, symbol.isMutated, isCrossingInlineBounds)
else -> CodeFragmentCapturedValue.Local(symbol.name, symbol.isMutated, isCrossingInlineBounds)
}
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2023 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.analysis.low.level.api.fir.compile
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.psi.impl.compiled.StubBuildingVisitor
import org.jetbrains.kotlin.analysis.providers.ForeignValueProviderService
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.caches.firCachesFactory
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildProperty
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack
import org.jetbrains.kotlin.fir.java.resolveIfJavaType
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.jvm.buildJavaTypeRef
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.org.objectweb.asm.Type
import java.text.StringCharacterIterator
val FirSession.codeFragmentScopeProvider: CodeFragmentScopeProvider by FirSession.sessionComponentAccessor()
private object ForeignValueMarkerDataKey : FirDeclarationDataKey()
var FirProperty.foreignValueMarker: Boolean? by FirDeclarationDataRegistry.data(ForeignValueMarkerDataKey)
class CodeFragmentScopeProvider(private val session: FirSession) : FirSessionComponent {
private val foreignValueProvider = ForeignValueProviderService.getInstance()
private val typeCache = session.firCachesFactory.createCache<String, FirTypeRef, KtCodeFragment> { typeDescriptor, ktCodeFragment ->
getPrimitiveType(typeDescriptor, session)?.let { return@createCache it }
val project = ktCodeFragment.project
val javaElementSourceFactory = JavaElementSourceFactory.getInstance(project)
val signatureIterator = StringCharacterIterator(typeDescriptor)
val typeString = SignatureParsing.parseTypeString(signatureIterator, StubBuildingVisitor.GUESSING_MAPPER)
val psiType = ClsTypeElementImpl(ktCodeFragment, typeString, '\u0000').type
val javaType = JavaTypeImpl.create(psiType, javaElementSourceFactory.createTypeSource(psiType))
val javaTypeRef = buildJavaTypeRef {
annotationBuilder = { emptyList() }
type = javaType
}
javaTypeRef.resolveIfJavaType(session, JavaTypeParameterStack.EMPTY)
}
fun getExtraScopes(codeFragment: KtCodeFragment): List<FirLocalScope> {
val foreignValues = foreignValueProvider?.getForeignValues(codeFragment)?.takeUnless { it.isEmpty() } ?: return emptyList()
return listOf(getForeignValuesScope(codeFragment, foreignValues))
}
private fun getForeignValuesScope(ktCodeFragment: KtCodeFragment, foreignValues: Map<String, String>): FirLocalScope {
var result = FirLocalScope(session)
for ((variableNameString, typeDescriptor) in foreignValues) {
val variableName = Name.identifier(variableNameString)
val variable = buildProperty {
resolvePhase = FirResolvePhase.BODY_RESOLVE
moduleData = session.moduleData
origin = FirDeclarationOrigin.Source
status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)
returnTypeRef = typeCache.getValue(typeDescriptor, ktCodeFragment)
deprecationsProvider = EmptyDeprecationsProvider
name = variableName
isVar = false
symbol = FirPropertySymbol(variableName)
isLocal = true
}
variable.foreignValueMarker = true
result = result.storeVariable(variable, session)
}
return result
}
}
private fun getPrimitiveType(typeDescriptor: String, session: FirSession): FirTypeRef? {
val asmType = Type.getType(typeDescriptor)
return when (asmType.sort) {
Type.VOID -> session.builtinTypes.unitType
Type.BOOLEAN -> session.builtinTypes.booleanType
Type.CHAR -> session.builtinTypes.charType
Type.BYTE -> session.builtinTypes.byteType
Type.SHORT -> session.builtinTypes.shortType
Type.INT -> session.builtinTypes.intType
Type.FLOAT -> session.builtinTypes.floatType
Type.LONG -> session.builtinTypes.longType
Type.DOUBLE -> session.builtinTypes.doubleType
else -> null
}
}
@@ -10,6 +10,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.resolve.extensions.KtResolveExtensionProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.IdeSessionComponents
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.createSealedInheritorsProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.CodeFragmentScopeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.fir.caches.FirThreadSafeCachesFactory
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirIdePredicateBasedProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.providers.LLFirIdeRegisteredPluginAnnotations
@@ -32,12 +33,9 @@ import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter
import org.jetbrains.kotlin.fir.extensions.FirPredicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.FirRegisteredPluginAnnotations
import org.jetbrains.kotlin.fir.java.FirJavaFacadeForSource
import org.jetbrains.kotlin.fir.java.JavaSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirCompositeSymbolProvider
import org.jetbrains.kotlin.fir.session.FirSessionConfigurator
import org.jetbrains.kotlin.load.java.createJavaClassFinder
@SessionConfiguration
internal fun LLFirSession.registerIdeComponents(project: Project) {
@@ -45,6 +43,7 @@ internal fun LLFirSession.registerIdeComponents(project: Project) {
register(FirCachesFactory::class, FirThreadSafeCachesFactory)
register(SealedClassInheritorsProvider::class, project.createSealedInheritorsProvider())
register(FirExceptionHandler::class, LLFirExceptionHandler)
register(CodeFragmentScopeProvider::class, CodeFragmentScopeProvider(this))
createResolveExtensionTool()?.let {
register(LLFirResolveExtensionTool::class, it)
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveT
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirWholeFileResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.asResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.analysis.low.level.api.fir.compile.codeFragmentScopeProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.element.builder.getNonLocalContainingOrThisDeclaration
import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider
import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.FirLazyBodiesCalculator
@@ -169,6 +170,11 @@ private class LLFirBodyTargetResolver(
val module = firCodeFragment.llFirModuleData.ktModule
val resolveSession = module.getFirResolveSession(ktCodeFragment.project) as LLFirResolvableResolveSession
fun FirTowerDataContext.withExtraScopes(): FirTowerDataContext {
return resolveSession.useSiteFirSession.codeFragmentScopeProvider.getExtraScopes(ktCodeFragment)
.fold(this) { context, scope -> context.addLocalScope(scope) }
}
val contextPsiElement = ktCodeFragment.context
val contextKtFile = contextPsiElement?.containingFile as? KtFile
@@ -189,9 +195,9 @@ private class LLFirBodyTargetResolver(
val elementContext = contextProvider[contextPsiElement, ContextCollector.ContextKind.BODY]
?: contextParentKtElements.firstNotNullOf { contextProvider[it, ContextCollector.ContextKind.SELF] }
LLFirCodeFragmentContext(elementContext.towerDataContext, elementContext.smartCasts)
LLFirCodeFragmentContext(elementContext.towerDataContext.withExtraScopes(), elementContext.smartCasts)
} else {
val towerDataContext = FirTowerDataContext()
val towerDataContext = FirTowerDataContext().withExtraScopes()
LLFirCodeFragmentContext(towerDataContext, emptyMap())
}
}