Refactor: extract resolve related code from ReplInterpreter to CliReplAnalyzerEngine

This commit is contained in:
Pavel V. Talanov
2016-04-06 18:37:52 +03:00
parent 68f1cf5f52
commit 3b4ade2ca0
3 changed files with 216 additions and 172 deletions
@@ -0,0 +1,191 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.getModuleName
import org.jetbrains.kotlin.cli.jvm.repl.di.createContainerForReplWithJava
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportsFactory
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.TopLevelDescriptorProvider
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.*
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.storage.StorageManager
class CliReplAnalyzerEngine(private val environment: KotlinCoreEnvironment) {
private val topDownAnalysisContext: TopDownAnalysisContext
private val topDownAnalyzer: LazyTopDownAnalyzerForTopLevel
private val resolveSession: ResolveSession
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
val module: ModuleDescriptorImpl
private var lastLineScope: LexicalScope? = null
val trace: BindingTraceContext = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
init {
val moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(environment.project, environment.getModuleName())
this.module = moduleContext.module
scriptDeclarationFactory = ScriptMutableDeclarationProviderFactory()
val container = createContainerForReplWithJava(
moduleContext,
trace,
scriptDeclarationFactory,
ProjectScope.getAllScope(environment.project),
object : ReplLastLineScopeProvider {
override val lastLineScope: LexicalScope?
get() = this@CliReplAnalyzerEngine.lastLineScope
},
JvmPackagePartProvider(environment)
)
this.topDownAnalysisContext = TopDownAnalysisContext(
TopDownAnalysisMode.LocalDeclarations, DataFlowInfoFactory.EMPTY, container.resolveSession.declarationScopeProvider
)
this.topDownAnalyzer = container.lazyTopDownAnalyzerForTopLevel
this.resolveSession = container.resolveSession
moduleContext.initializeModuleContents(CompositePackageFragmentProvider(
listOf(
container.resolveSession.packageFragmentProvider,
container.javaDescriptorResolver.packageFragmentProvider
)
))
}
interface ReplLineAnalysisResult {
val scriptDescriptor: ScriptDescriptor?
val diagnostics: Diagnostics
data class Successful(override val scriptDescriptor: ScriptDescriptor, override val diagnostics: Diagnostics) : ReplLineAnalysisResult
data class WithErrors(override val diagnostics: Diagnostics) : ReplLineAnalysisResult {
override val scriptDescriptor: ScriptDescriptor? get() = null
}
}
fun analyzeReplLine(psiFile: KtFile, priority: Int): ReplLineAnalysisResult {
topDownAnalysisContext.scripts.clear()
trace.clearDiagnostics()
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, priority)
return doAnalyze(psiFile)
}
private fun doAnalyze(psiFile: KtFile): ReplLineAnalysisResult {
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(psiFile)))
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(psiFile))
if (trace.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, psiFile) == null) {
trace.record(BindingContext.FILE_TO_PACKAGE_FRAGMENT, psiFile, resolveSession.getPackageFragment(FqName.ROOT))
}
val diagnostics = trace.bindingContext.diagnostics
val hasErrors = diagnostics.any { it.severity == Severity.ERROR }
if (hasErrors) {
return ReplLineAnalysisResult.WithErrors(diagnostics)
}
else {
val scriptDescriptor = context.scripts[psiFile.script]!!.apply {
lastLineScope = this.scopeForInitializerResolution
}
return ReplLineAnalysisResult.Successful(scriptDescriptor, diagnostics)
}
}
private class ScriptMutableDeclarationProviderFactory : DeclarationProviderFactory {
private lateinit var delegateFactory: DeclarationProviderFactory
private lateinit var rootPackageProvider: AdaptablePackageMemberDeclarationProvider
fun setDelegateFactory(delegateFactory: DeclarationProviderFactory) {
this.delegateFactory = delegateFactory
val provider = delegateFactory.getPackageMemberDeclarationProvider(FqName.ROOT)!!
try {
rootPackageProvider.addDelegateProvider(provider)
}
catch (e: UninitializedPropertyAccessException) {
rootPackageProvider = AdaptablePackageMemberDeclarationProvider(provider)
}
}
override fun getClassMemberDeclarationProvider(classLikeInfo: KtClassLikeInfo): ClassMemberDeclarationProvider {
return delegateFactory.getClassMemberDeclarationProvider(classLikeInfo)
}
override fun getPackageMemberDeclarationProvider(packageFqName: FqName): PackageMemberDeclarationProvider? {
if (packageFqName.isRoot) {
return rootPackageProvider
}
return delegateFactory.getPackageMemberDeclarationProvider(packageFqName)
}
override fun diagnoseMissingPackageFragment(file: KtFile) {
delegateFactory.diagnoseMissingPackageFragment(file)
}
class AdaptablePackageMemberDeclarationProvider(
private var delegateProvider: PackageMemberDeclarationProvider
) : DelegatePackageMemberDeclarationProvider(delegateProvider) {
fun addDelegateProvider(provider: PackageMemberDeclarationProvider) {
delegateProvider = CombinedPackageMemberDeclarationProvider(listOf(provider, delegateProvider))
delegate = delegateProvider
}
}
}
}
interface ReplLastLineScopeProvider {
val lastLineScope: LexicalScope?
}
class ReplFileScopeProvider(
private val lastLineScopeProvider: ReplLastLineScopeProvider,
topLevelDescriptorProvider: TopLevelDescriptorProvider,
storageManager: StorageManager,
moduleDescriptor: ModuleDescriptor,
qualifiedExpressionResolver: QualifiedExpressionResolver,
bindingTrace: BindingTrace,
ktImportsFactory: KtImportsFactory
) : FileScopeProviderImpl(topLevelDescriptorProvider, storageManager, moduleDescriptor, qualifiedExpressionResolver, bindingTrace, ktImportsFactory) {
override fun getFileResolutionScope(file: KtFile): LexicalScope
= lastLineScopeProvider.lastLineScope ?: super.getFileResolutionScope(file)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,18 +22,13 @@ import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.impl.PsiFileFactoryImpl
import com.intellij.psi.search.ProjectScope
import com.intellij.testFramework.LightVirtualFile
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.DiagnosticMessageReporter
import org.jetbrains.kotlin.cli.jvm.compiler.CliLightClassGenerationSupport
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.getModuleName
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.repl.di.ReplLastLineScopeProvider
import org.jetbrains.kotlin.cli.jvm.repl.di.createContainerForReplWithJava
import org.jetbrains.kotlin.cli.jvm.repl.CliReplAnalyzerEngine.ReplLineAnalysisResult.Successful
import org.jetbrains.kotlin.cli.jvm.repl.CliReplAnalyzerEngine.ReplLineAnalysisResult.WithErrors
import org.jetbrains.kotlin.cli.jvm.repl.messages.DiagnosticMessageHolder
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplIdeDiagnosticMessageHolder
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemInWrapper
@@ -45,25 +40,14 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.data.KtClassLikeInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.*
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptParameter
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.script.StandardScriptDefinition
import java.io.PrintWriter
import java.net.URLClassLoader
@@ -76,63 +60,21 @@ class ReplInterpreter(
) {
private var lineNumber = 0
private var lastLineScope: LexicalScope? = null
private val earlierLines = arrayListOf<EarlierLine>()
private val previousIncompleteLines = arrayListOf<String>()
private val classLoader: ReplClassLoader
private val psiFileFactory: PsiFileFactoryImpl
private val trace: BindingTraceContext
private val module: ModuleDescriptorImpl
private val topDownAnalysisContext: TopDownAnalysisContext
private val topDownAnalyzer: LazyTopDownAnalyzerForTopLevel
private val resolveSession: ResolveSession
private val scriptDeclarationFactory: ScriptMutableDeclarationProviderFactory
init {
configuration.add(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY, REPL_LINE_AS_SCRIPT_DEFINITION)
val environment = KotlinCoreEnvironment.createForProduction(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val project = environment.project
this.psiFileFactory = PsiFileFactory.getInstance(project) as PsiFileFactoryImpl
this.trace = CliLightClassGenerationSupport.NoScopeRecordCliBindingTrace()
val moduleContext = TopDownAnalyzerFacadeForJVM.createContextWithSealedModule(project, environment.getModuleName())
this.module = moduleContext.module
scriptDeclarationFactory = ScriptMutableDeclarationProviderFactory()
val container = createContainerForReplWithJava(
moduleContext,
trace,
scriptDeclarationFactory,
ProjectScope.getAllScope(project),
object : ReplLastLineScopeProvider {
override val lastLineScope: LexicalScope?
get() = this@ReplInterpreter.lastLineScope
},
JvmPackagePartProvider(environment)
)
this.topDownAnalysisContext = TopDownAnalysisContext(
TopDownAnalysisMode.LocalDeclarations, DataFlowInfoFactory.EMPTY, container.resolveSession.declarationScopeProvider
)
this.topDownAnalyzer = container.lazyTopDownAnalyzerForTopLevel
this.resolveSession = container.resolveSession
moduleContext.initializeModuleContents(CompositePackageFragmentProvider(
listOf(
container.resolveSession.packageFragmentProvider,
container.javaDescriptorResolver.packageFragmentProvider
)
))
private val classLoader: ReplClassLoader = run {
val classpath = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
this.classLoader = ReplClassLoader(URLClassLoader(classpath.toTypedArray(), null))
ReplClassLoader(URLClassLoader(classpath.toTypedArray(), null))
}
private val environment = run {
configuration.add(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY, REPL_LINE_AS_SCRIPT_DEFINITION)
KotlinCoreEnvironment.createForProduction(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
}
private val psiFileFactory: PsiFileFactoryImpl = PsiFileFactory.getInstance(environment.project) as PsiFileFactoryImpl
private val analyzerEngine = CliReplAnalyzerEngine(environment)
enum class LineResultType {
SUCCESS,
COMPILE_ERROR,
@@ -231,16 +173,17 @@ class ReplInterpreter(
return LineResult.compileError(errorHolder.renderedDiagnostics)
}
prepareForTheNextReplLine(topDownAnalysisContext)
trace.clearDiagnostics()
psiFile.script!!.putUserData(ScriptPriorities.PRIORITY_KEY, lineNumber)
val scriptDescriptor = doAnalyze(psiFile, errorHolder)
?: return LineResult.compileError(errorHolder.renderedDiagnostics)
val analysisResult = analyzerEngine.analyzeReplLine(psiFile, lineNumber)
AnalyzerWithCompilerReport.reportDiagnostics(analysisResult.diagnostics, errorHolder, false)
val scriptDescriptor = when (analysisResult) {
is WithErrors -> return LineResult.compileError(errorHolder.renderedDiagnostics)
is Successful -> analysisResult.scriptDescriptor
else -> error("Unexpected result ${analysisResult.javaClass}")
}
val state = GenerationState(
psiFile.project, ClassBuilderFactories.BINARIES, module, trace.bindingContext, listOf(psiFile), configuration
psiFile.project, ClassBuilderFactories.BINARIES, analyzerEngine.module,
analyzerEngine.trace.bindingContext, listOf(psiFile), configuration
)
compileScript(psiFile.script!!, earlierLines.map(EarlierLine::getScriptDescriptor), state, CompilationErrorHandler.THROW_EXCEPTION)
@@ -285,72 +228,10 @@ class ReplInterpreter(
}
}
private fun doAnalyze(psiFile: KtFile, errorReporter: DiagnosticMessageReporter): ScriptDescriptor? {
scriptDeclarationFactory.setDelegateFactory(FileBasedDeclarationProviderFactory(resolveSession.storageManager, listOf(psiFile)))
val context = topDownAnalyzer.analyzeDeclarations(topDownAnalysisContext.topDownAnalysisMode, listOf(psiFile))
if (trace.get(BindingContext.FILE_TO_PACKAGE_FRAGMENT, psiFile) == null) {
trace.record(BindingContext.FILE_TO_PACKAGE_FRAGMENT, psiFile, resolveSession.getPackageFragment(FqName.ROOT))
}
val hasErrors = AnalyzerWithCompilerReport.reportDiagnostics(trace.bindingContext.diagnostics, errorReporter, false)
if (hasErrors) {
return null
}
return context.scripts[psiFile.script]?.apply {
lastLineScope = this.scopeForInitializerResolution
}
}
fun dumpClasses(out: PrintWriter) {
classLoader.dumpClasses(out)
}
private class ScriptMutableDeclarationProviderFactory : DeclarationProviderFactory {
private lateinit var delegateFactory: DeclarationProviderFactory
private lateinit var rootPackageProvider: AdaptablePackageMemberDeclarationProvider
fun setDelegateFactory(delegateFactory: DeclarationProviderFactory) {
this.delegateFactory = delegateFactory
val provider = delegateFactory.getPackageMemberDeclarationProvider(FqName.ROOT)!!
try {
rootPackageProvider.addDelegateProvider(provider)
}
catch (e: UninitializedPropertyAccessException) {
rootPackageProvider = AdaptablePackageMemberDeclarationProvider(provider)
}
}
override fun getClassMemberDeclarationProvider(classLikeInfo: KtClassLikeInfo): ClassMemberDeclarationProvider {
return delegateFactory.getClassMemberDeclarationProvider(classLikeInfo)
}
override fun getPackageMemberDeclarationProvider(packageFqName: FqName): PackageMemberDeclarationProvider? {
if (packageFqName.isRoot) {
return rootPackageProvider
}
return delegateFactory.getPackageMemberDeclarationProvider(packageFqName)
}
override fun diagnoseMissingPackageFragment(file: KtFile) {
delegateFactory.diagnoseMissingPackageFragment(file)
}
class AdaptablePackageMemberDeclarationProvider(
private var delegateProvider: PackageMemberDeclarationProvider
) : DelegatePackageMemberDeclarationProvider(delegateProvider) {
fun addDelegateProvider(provider: PackageMemberDeclarationProvider) {
delegateProvider = CombinedPackageMemberDeclarationProvider(listOf(provider, delegateProvider))
delegate = delegateProvider
}
}
}
companion object {
private val SCRIPT_RESULT_FIELD_NAME = "\$\$result"
private val REPL_LINE_AS_SCRIPT_DEFINITION = object : KotlinScriptDefinition {
@@ -361,10 +242,6 @@ class ReplInterpreter(
override fun getScriptName(script: KtScript): Name = StandardScriptDefinition.getScriptName(script)
}
private fun prepareForTheNextReplLine(c: TopDownAnalysisContext) {
c.scripts.clear()
}
private fun renderStackTrace(cause: Throwable): String {
val newTrace = arrayListOf<StackTraceElement>()
var skip = true
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,48 +17,24 @@
package org.jetbrains.kotlin.cli.jvm.repl.di
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.cli.jvm.repl.ReplFileScopeProvider
import org.jetbrains.kotlin.cli.jvm.repl.ReplLastLineScopeProvider
import org.jetbrains.kotlin.container.*
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackagePartProvider
import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.frontend.java.di.configureJavaTopDownAnalysis
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.load.java.JavaClassFinderImpl
import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportsFactory
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzerForTopLevel
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver
import org.jetbrains.kotlin.resolve.jvm.JavaClassFinderPostConstruct
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.TopLevelDescriptorProvider
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.storage.StorageManager
interface ReplLastLineScopeProvider {
val lastLineScope: LexicalScope?
}
class ReplFileScopeProvider(
private val lastLineScopeProvider: ReplLastLineScopeProvider,
topLevelDescriptorProvider: TopLevelDescriptorProvider,
storageManager: StorageManager,
moduleDescriptor: ModuleDescriptor,
qualifiedExpressionResolver: QualifiedExpressionResolver,
bindingTrace: BindingTrace,
ktImportsFactory: KtImportsFactory
) : FileScopeProviderImpl(topLevelDescriptorProvider, storageManager, moduleDescriptor, qualifiedExpressionResolver, bindingTrace, ktImportsFactory) {
override fun getFileResolutionScope(file: KtFile): LexicalScope
= lastLineScopeProvider.lastLineScope ?: super.getFileResolutionScope(file)
}
fun createContainerForReplWithJava(
moduleContext: ModuleContext,