From 8a5cab6831e342901633db77935e374bf96a04a9 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 7 Jul 2023 22:22:01 +0900 Subject: [PATCH] [LL API] Support '_DebugLabel's in code fragments --- .../providers/ForeignValueProviderService.kt | 19 +++ .../CodeFragmentCapturedValueAnalyzer.kt | 1 + .../fir/compile/CodeFragmentScopeProvider.kt | 109 ++++++++++++++++++ .../structure/sessionFactoryHelpers.kt | 5 +- .../fir/transformers/LLFirBodyLazyResolver.kt | 10 +- 5 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/ForeignValueProviderService.kt create mode 100644 analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentScopeProvider.kt diff --git a/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/ForeignValueProviderService.kt b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/ForeignValueProviderService.kt new file mode 100644 index 00000000000..dd21937c336 --- /dev/null +++ b/analysis/analysis-api-providers/src/org/jetbrains/kotlin/analysis/providers/ForeignValueProviderService.kt @@ -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 + + public companion object { + public fun getInstance(): ForeignValueProviderService? { + return ApplicationManager.getApplication().getService(ForeignValueProviderService::class.java) + } + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt index c55dfca4979..5a8cebd8e16 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentCapturedValueAnalyzer.kt @@ -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) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentScopeProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentScopeProvider.kt new file mode 100644 index 00000000000..860deb1bb18 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compile/CodeFragmentScopeProvider.kt @@ -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 { 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 { + val foreignValues = foreignValueProvider?.getForeignValues(codeFragment)?.takeUnless { it.isEmpty() } ?: return emptyList() + return listOf(getForeignValuesScope(codeFragment, foreignValues)) + } + + private fun getForeignValuesScope(ktCodeFragment: KtCodeFragment, foreignValues: Map): 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 + } +} \ No newline at end of file diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt index 60c9085d26a..38b759fb072 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/project/structure/sessionFactoryHelpers.kt @@ -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) } diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt index a44452e4d99..e1130ce8f09 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/LLFirBodyLazyResolver.kt @@ -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()) } }