[LL API] Support '_DebugLabel's in code fragments
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentCapturedValueAnalyzer.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentCapturedValueAnalyzer.kt index 8c594f9..ed754d5 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentCapturedValueAnalyzer.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentCapturedValueAnalyzer.kt
@@ -6,7 +6,6 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.compiler import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.analysis.low.level.api.fir.api.LLFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol @@ -163,6 +162,7 @@ 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/compiler/CodeFragmentScopeProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentScopeProvider.kt new file mode 100644 index 0000000..9caaa2a --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/CodeFragmentScopeProvider.kt
@@ -0,0 +1,108 @@ +/* + * 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.compiler + +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.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 + +internal val FirSession.codeFragmentScopeProvider: CodeFragmentScopeProvider by FirSession.sessionComponentAccessor() + +private object ForeignValueMarkerDataKey : FirDeclarationDataKey() + +internal var FirProperty.foreignValueMarker: Boolean? by FirDeclarationDataRegistry.data(ForeignValueMarkerDataKey) + +internal 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 + } +} \ No newline at end of file
diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/ForeignValueProviderService.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/ForeignValueProviderService.kt new file mode 100644 index 0000000..27aff87 --- /dev/null +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/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.low.level.api.fir.compiler + +import com.intellij.openapi.application.ApplicationManager +import org.jetbrains.kotlin.psi.KtCodeFragment + +interface ForeignValueProviderService { + fun getForeignValues(codeFragment: KtCodeFragment): Map<String, String> + + companion object { + 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/compiler/LLCompilerFacade.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/LLCompilerFacade.kt index 7bf8940..80ba049 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/LLCompilerFacade.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/compiler/LLCompilerFacade.kt
@@ -7,6 +7,7 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.util.Key import org.jetbrains.kotlin.analysis.low.level.api.fir.api.* import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableModuleSession import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider @@ -51,7 +52,6 @@ import org.jetbrains.kotlin.resolve.source.PsiSourceFile import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.utils.addToStdlib.runIf -import org.jetbrains.org.objectweb.asm.Type class LLCompilationResult( val outputFiles: List<OutputFile>, @@ -66,9 +66,6 @@ /** Entry point method name for the code fragment. */ val CODE_FRAGMENT_METHOD_NAME = CompilerConfigurationKey<String>("code fragment method name") - /** '_DebugLabel' mappings for the code fragment. */ - val CODE_FRAGMENT_DEBUG_LABELS = CompilerConfigurationKey<Map<String, Type>>("code fragment '_DebugLabel' entries") - fun compile( file: KtFile, configuration: CompilerConfiguration,
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 60c9085..96214d6 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 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.compiler.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.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 @@ 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/FirCodeFragmentResolveContextCollector.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/FirCodeFragmentResolveContextCollector.kt index 9e1da4a..ed7254d 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/FirCodeFragmentResolveContextCollector.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/transformers/FirCodeFragmentResolveContextCollector.kt
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.BodyResolveContext import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirResolveContextCollector +import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.psi.KtElement @@ -20,7 +21,8 @@ internal class FirCodeFragmentResolveContextCollector( private val sessionHolder: SessionHolder, - contextElement: KtElement + private val extraScopes: List<FirLocalScope>, + contextElement: KtElement, ) : FirResolveContextCollector { private val contextElementCandidates = contextElement.parentsWithSelf.toSet() private val elementContexts = hashMapOf<KtElement, FirCodeFragmentContext>() @@ -30,7 +32,7 @@ override fun addFileContext(file: FirFile, context: FirTowerDataContext) { val psiFile = file.psi as? KtFile ?: return - elementContexts[psiFile] = FileFirCodeFragmentContext(context) + elementContexts[psiFile] = FileFirCodeFragmentContext(context.includingExtraScopes()) } override fun addStatementContext(statement: FirStatement, context: BodyResolveContext) { @@ -73,7 +75,11 @@ } } - return StatementFirCodeFragmentContext(towerDataContext, variables) + return StatementFirCodeFragmentContext(towerDataContext.includingExtraScopes(), variables) + } + + private fun FirTowerDataContext.includingExtraScopes(): FirTowerDataContext { + return extraScopes.fold(this) { context, scope -> context.addLocalScope(scope) } } }
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 e92c0b8..30cf759 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
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getFirResolveSession import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError +import org.jetbrains.kotlin.analysis.low.level.api.fir.compiler.codeFragmentScopeProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.file.builder.LLFirLockProvider import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.LLFirPhaseUpdater import org.jetbrains.kotlin.analysis.low.level.api.fir.lazy.resolve.RawFirReplacement @@ -174,7 +175,8 @@ val module = codeFragment.llFirModuleData.ktModule val resolveSession = module.getFirResolveSession(ktFile.project) as LLFirResolvableResolveSession - val collector = FirCodeFragmentResolveContextCollector(transformer.components, contextElement) + val extraScopes = resolveSession.useSiteFirSession.codeFragmentScopeProvider.getExtraScopes(ktFile) + val collector = FirCodeFragmentResolveContextCollector(transformer.components, extraScopes, contextElement) val replacement = RawFirReplacement(contextElement, contextElement) LowLevelFirApiFacadeForResolveOnAir.runBodyResolveOnAir(resolveSession, replacement, onAirCreatedDeclaration = false, collector) codeFragment.codeFragmentContext = collector.context