diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JetCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JetCoreEnvironment.kt index 901c4f5683a..8efdffdec69 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JetCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/JetCoreEnvironment.kt @@ -14,404 +14,354 @@ * limitations under the License. */ -package org.jetbrains.kotlin.cli.jvm.compiler; +package org.jetbrains.kotlin.cli.jvm.compiler -import com.google.common.collect.Sets; -import com.intellij.codeInsight.ContainerProvider; -import com.intellij.codeInsight.ExternalAnnotationsManager; -import com.intellij.codeInsight.runner.JavaMainMethodProvider; -import com.intellij.core.CoreApplicationEnvironment; -import com.intellij.core.CoreJavaFileManager; -import com.intellij.core.JavaCoreApplicationEnvironment; -import com.intellij.core.JavaCoreProjectEnvironment; -import com.intellij.lang.java.JavaParserDefinition; -import com.intellij.mock.MockApplication; -import com.intellij.mock.MockProject; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.extensions.ExtensionsArea; -import com.intellij.openapi.fileTypes.ContentBasedFileSubstitutor; -import com.intellij.openapi.fileTypes.FileTypeExtensionPoint; -import com.intellij.openapi.fileTypes.PlainTextFileType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.FileContextProvider; -import com.intellij.psi.PsiElementFinder; -import com.intellij.psi.PsiManager; -import com.intellij.psi.augment.PsiAugmentProvider; -import com.intellij.psi.compiled.ClassFileDecompilers; -import com.intellij.psi.impl.PsiElementFinderImpl; -import com.intellij.psi.impl.PsiTreeChangePreprocessor; -import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy; -import com.intellij.psi.impl.compiled.ClsStubBuilderFactory; -import com.intellij.psi.impl.file.impl.JavaFileManager; -import com.intellij.psi.meta.MetaDataContributor; -import com.intellij.psi.stubs.BinaryFileStubBuilders; -import com.intellij.util.containers.ContainerUtil; -import kotlin.Function1; -import kotlin.Unit; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; -import org.jetbrains.kotlin.asJava.JavaElementFinder; -import org.jetbrains.kotlin.asJava.KotlinLightClassForPackage; -import org.jetbrains.kotlin.asJava.LightClassGenerationSupport; -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; -import org.jetbrains.kotlin.cli.common.messages.MessageCollector; -import org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys; -import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; -import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar; -import org.jetbrains.kotlin.config.CommonConfigurationKeys; -import org.jetbrains.kotlin.config.CompilerConfiguration; -import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider; -import org.jetbrains.kotlin.idea.JetFileType; -import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache; -import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory; -import org.jetbrains.kotlin.parsing.JetParserDefinition; -import org.jetbrains.kotlin.parsing.JetScriptDefinitionProvider; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer; -import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade; -import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService; -import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService; -import org.jetbrains.kotlin.utils.PathUtil; +import com.google.common.collect.Sets +import com.intellij.codeInsight.ContainerProvider +import com.intellij.codeInsight.ExternalAnnotationsManager +import com.intellij.codeInsight.runner.JavaMainMethodProvider +import com.intellij.core.CoreApplicationEnvironment +import com.intellij.core.CoreJavaFileManager +import com.intellij.core.JavaCoreApplicationEnvironment +import com.intellij.core.JavaCoreProjectEnvironment +import com.intellij.lang.java.JavaParserDefinition +import com.intellij.mock.MockApplication +import com.intellij.mock.MockProject +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.Application +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.extensions.ExtensionsArea +import com.intellij.openapi.fileTypes.ContentBasedFileSubstitutor +import com.intellij.openapi.fileTypes.FileTypeExtensionPoint +import com.intellij.openapi.fileTypes.PlainTextFileType +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.FileContextProvider +import com.intellij.psi.PsiElementFinder +import com.intellij.psi.PsiManager +import com.intellij.psi.augment.PsiAugmentProvider +import com.intellij.psi.compiled.ClassFileDecompilers +import com.intellij.psi.impl.PsiElementFinderImpl +import com.intellij.psi.impl.PsiTreeChangePreprocessor +import com.intellij.psi.impl.compiled.ClsCustomNavigationPolicy +import com.intellij.psi.impl.compiled.ClsStubBuilderFactory +import com.intellij.psi.impl.file.impl.JavaFileManager +import com.intellij.psi.meta.MetaDataContributor +import com.intellij.psi.stubs.BinaryFileStubBuilders +import com.intellij.util.containers.ContainerUtil +import kotlin.Function1 +import kotlin.Unit +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.asJava.JavaElementFinder +import org.jetbrains.kotlin.asJava.KotlinLightClassForPackage +import org.jetbrains.kotlin.asJava.LightClassGenerationSupport +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys +import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension +import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar +import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.extensions.ExternalDeclarationsProvider +import org.jetbrains.kotlin.idea.JetFileType +import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache +import org.jetbrains.kotlin.load.kotlin.VirtualFileFinderFactory +import org.jetbrains.kotlin.parsing.JetParserDefinition +import org.jetbrains.kotlin.parsing.JetScriptDefinitionProvider +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer +import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade +import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService +import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService +import org.jetbrains.kotlin.utils.PathUtil -import java.io.File; -import java.util.*; +import java.io.File +import java.util.* -import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR; -import static org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING; +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING -@SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") -public class JetCoreEnvironment { +SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") +public class JetCoreEnvironment private(parentDisposable: Disposable, applicationEnvironment: JavaCoreApplicationEnvironment, configuration: CompilerConfiguration) { - private static final Object APPLICATION_LOCK = new Object(); - private static JavaCoreApplicationEnvironment ourApplicationEnvironment; - private static int ourProjectCount = 0; + private val projectEnvironment: JavaCoreProjectEnvironment + private val sourceFiles = ArrayList() + private val classPath = ClassPath() - @NotNull - public static JetCoreEnvironment createForProduction( - @NotNull Disposable parentDisposable, - @NotNull CompilerConfiguration configuration, - @NotNull List configFilePaths - ) { - // JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ) - // All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well - Disposer.register(parentDisposable, new Disposable() { - @Override - public void dispose() { - synchronized (APPLICATION_LOCK) { - if (--ourProjectCount <= 0) { - disposeApplicationEnvironment(); - } - } + private val annotationsManager: CoreExternalAnnotationsManager + + public val configuration: CompilerConfiguration + + { + this.configuration = configuration.copy() + this.configuration.setReadOnly(true) + + projectEnvironment = object : JavaCoreProjectEnvironment(parentDisposable, applicationEnvironment) { + override fun preregisterServices() { + registerProjectExtensionPoints(Extensions.getArea(getProject())) } - }); - JetCoreEnvironment environment = - new JetCoreEnvironment(parentDisposable, getOrCreateApplicationEnvironmentForProduction(configuration, configFilePaths), configuration); - - synchronized (APPLICATION_LOCK) { - ourProjectCount++; - } - return environment; - } - - @TestOnly - @NotNull - public static JetCoreEnvironment createForTests( - @NotNull Disposable parentDisposable, - @NotNull CompilerConfiguration configuration, - @NotNull List extensionConfigs - ) { - // Tests are supposed to create a single project and dispose it right after use - return new JetCoreEnvironment(parentDisposable, createApplicationEnvironment(parentDisposable, configuration, extensionConfigs), configuration); - } - - @NotNull - private static JavaCoreApplicationEnvironment getOrCreateApplicationEnvironmentForProduction( - @NotNull CompilerConfiguration configuration, - @NotNull List configFilePaths) { - synchronized (APPLICATION_LOCK) { - if (ourApplicationEnvironment != null) return ourApplicationEnvironment; - - Disposable parentDisposable = Disposer.newDisposable(); - ourApplicationEnvironment = createApplicationEnvironment(parentDisposable, configuration, configFilePaths); - ourProjectCount = 0; - Disposer.register(parentDisposable, new Disposable() { - @Override - public void dispose() { - synchronized (APPLICATION_LOCK) { - ourApplicationEnvironment = null; - } - } - }); - return ourApplicationEnvironment; - } - } - - public static void disposeApplicationEnvironment() { - synchronized (APPLICATION_LOCK) { - if (ourApplicationEnvironment == null) return; - JavaCoreApplicationEnvironment environment = ourApplicationEnvironment; - ourApplicationEnvironment = null; - Disposer.dispose(environment.getParentDisposable()); - } - } - - @NotNull - private static JavaCoreApplicationEnvironment createApplicationEnvironment( - @NotNull Disposable parentDisposable, - @NotNull CompilerConfiguration configuration, - @NotNull List configFilePaths - ) { - Extensions.cleanRootArea(parentDisposable); - registerAppExtensionPoints(); - JavaCoreApplicationEnvironment applicationEnvironment = new JavaCoreApplicationEnvironment(parentDisposable); - - for (String configPath : configFilePaths) { - registerApplicationExtensionPointsAndExtensionsFrom(configuration, configPath); } - registerApplicationServicesForCLI(applicationEnvironment); - registerApplicationServices(applicationEnvironment); + val project = projectEnvironment.getProject() + annotationsManager = CoreExternalAnnotationsManager(project.getComponent(javaClass())) + project.registerService(javaClass(), annotationsManager) + project.registerService(javaClass(), CliDeclarationProviderFactoryService(sourceFiles)) - return applicationEnvironment; - } + registerProjectServicesForCLI(projectEnvironment) + registerProjectServices(projectEnvironment) - private static void registerAppExtensionPoints() { - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContentBasedFileSubstitutor.EP_NAME, ContentBasedFileSubstitutor.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), BinaryFileStubBuilders.EP_NAME, FileTypeExtensionPoint.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), FileContextProvider.EP_NAME, FileContextProvider.class); - // - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaDataContributor.EP_NAME, MetaDataContributor.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsStubBuilderFactory.EP_NAME, ClsStubBuilderFactory.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, PsiAugmentProvider.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, JavaMainMethodProvider.class); - // - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContainerProvider.EP_NAME, ContainerProvider.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME, ClsCustomNavigationPolicy.class); - CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, - ClassFileDecompilers.Decompiler.class); - } - - private static void registerApplicationExtensionPointsAndExtensionsFrom(@NotNull CompilerConfiguration configuration, @NotNull String configFilePath) { - CompilerJarLocator locator = configuration.get(JVMConfigurationKeys.COMPILER_JAR_LOCATOR); - File pluginRoot = locator == null ? PathUtil.getPathUtilJar() : locator.getCompilerJar(); - - Application app = ApplicationManager.getApplication(); - File parentFile = pluginRoot.getParentFile(); - - if (pluginRoot.isDirectory() && - app != null && app.isUnitTestMode() && - FileUtil.toCanonicalPath(parentFile.getPath()).endsWith("out/production") - ) { - // hack for load extensions when compiler run directly from out directory(e.g. in tests) - File srcDir = parentFile.getParentFile().getParentFile(); - pluginRoot = new File(srcDir, "idea/src"); + for (path in configuration.getList(JVMConfigurationKeys.CLASSPATH_KEY)) { + addToClasspath(path) } - - CoreApplicationEnvironment.registerExtensionPointAndExtensions(pluginRoot, configFilePath, Extensions.getRootArea()); - } - - private static void registerApplicationServicesForCLI(@NotNull JavaCoreApplicationEnvironment applicationEnvironment) { - // ability to get text from annotations xml files - applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml"); - applicationEnvironment.registerParserDefinition(new JavaParserDefinition()); - } - - // made public for Upsource - public static void registerApplicationServices(@NotNull JavaCoreApplicationEnvironment applicationEnvironment) { - applicationEnvironment.registerFileType(JetFileType.INSTANCE, "kt"); - applicationEnvironment.registerFileType(JetFileType.INSTANCE, "ktm"); - applicationEnvironment.registerFileType(JetFileType.INSTANCE, JetParserDefinition.STD_SCRIPT_SUFFIX); // should be renamed to kts - applicationEnvironment.registerParserDefinition(new JetParserDefinition()); - - applicationEnvironment.getApplication().registerService(KotlinBinaryClassCache.class, new KotlinBinaryClassCache()); - } - - private final JavaCoreProjectEnvironment projectEnvironment; - private final List sourceFiles = new ArrayList(); - private final ClassPath classPath = new ClassPath(); - - private final CoreExternalAnnotationsManager annotationsManager; - - private final CompilerConfiguration configuration; - - private JetCoreEnvironment( - @NotNull Disposable parentDisposable, - @NotNull JavaCoreApplicationEnvironment applicationEnvironment, - @NotNull CompilerConfiguration configuration - ) { - this.configuration = configuration.copy(); - this.configuration.setReadOnly(true); - - projectEnvironment = new JavaCoreProjectEnvironment(parentDisposable, applicationEnvironment){ - @Override - protected void preregisterServices() { - registerProjectExtensionPoints(Extensions.getArea(getProject())); + for (path in configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) { + addExternalAnnotationsRoot(path) + } + sourceFiles.addAll(CompileEnvironmentUtil.getJetFiles(getProject(), getSourceRootsCheckingForDuplicates(), object : Function1 { + override fun invoke(s: String): Unit { + report(ERROR, s) + return Unit.`INSTANCE$` } - }; + })) - MockProject project = projectEnvironment.getProject(); - annotationsManager = new CoreExternalAnnotationsManager(project.getComponent(PsiManager.class)); - project.registerService(ExternalAnnotationsManager.class, annotationsManager); - project.registerService(DeclarationProviderFactoryService.class, new CliDeclarationProviderFactoryService(sourceFiles)); - - registerProjectServicesForCLI(projectEnvironment); - registerProjectServices(projectEnvironment); - - for (File path : configuration.getList(JVMConfigurationKeys.CLASSPATH_KEY)) { - addToClasspath(path); - } - for (File path : configuration.getList(JVMConfigurationKeys.ANNOTATIONS_PATH_KEY)) { - addExternalAnnotationsRoot(path); - } - sourceFiles.addAll( - CompileEnvironmentUtil.getJetFiles( - getProject(), - getSourceRootsCheckingForDuplicates(), - new Function1() { - @Override - public Unit invoke(String s) { - report(ERROR, s); - return Unit.INSTANCE$; - } - } - ) - ); - - ContainerUtil.sort(sourceFiles, new Comparator() { - @Override - public int compare(@NotNull JetFile o1, @NotNull JetFile o2) { - return o1.getVirtualFile().getPath().compareToIgnoreCase(o2.getVirtualFile().getPath()); + ContainerUtil.sort(sourceFiles, object : Comparator { + override fun compare(o1: JetFile, o2: JetFile): Int { + return o1.getVirtualFile().getPath().compareToIgnoreCase(o2.getVirtualFile().getPath()) } - }); + }) - JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions( - configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY) - ); + JetScriptDefinitionProvider.getInstance(project).addScriptDefinitions(configuration.getList(CommonConfigurationKeys.SCRIPT_DEFINITIONS_KEY)) - project.registerService(VirtualFileFinderFactory.class, new CliVirtualFileFinderFactory(classPath)); + project.registerService(javaClass(), CliVirtualFileFinderFactory(classPath)) - ExternalDeclarationsProvider.Companion.registerExtensionPoint(project); - ExpressionCodegenExtension.Companion.registerExtensionPoint(project); + ExternalDeclarationsProvider.registerExtensionPoint(project) + ExpressionCodegenExtension.registerExtensionPoint(project) - for (ComponentRegistrar registrar : configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { - registrar.registerProjectComponents(project, configuration); + for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { + registrar.registerProjectComponents(project, configuration) } } - private static void registerProjectExtensionPoints(ExtensionsArea area) { - CoreApplicationEnvironment.registerExtensionPoint(area, PsiTreeChangePreprocessor.EP_NAME, PsiTreeChangePreprocessor.class); - CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP_NAME, PsiElementFinder.class); + private fun getMyApplicationEnvironment(): CoreApplicationEnvironment { + return projectEnvironment.getEnvironment() } - // made public for Upsource - public static void registerProjectServices(@NotNull JavaCoreProjectEnvironment projectEnvironment) { - MockProject project = projectEnvironment.getProject(); - project.registerService(JetScriptDefinitionProvider.class, new JetScriptDefinitionProvider()); - - project.registerService(KotlinJavaPsiFacade.class, new KotlinJavaPsiFacade(project)); - project.registerService(KotlinLightClassForPackage.FileStubCache.class, new KotlinLightClassForPackage.FileStubCache(project)); + public fun getApplication(): MockApplication { + return getMyApplicationEnvironment().getApplication() } - private static void registerProjectServicesForCLI(@NotNull JavaCoreProjectEnvironment projectEnvironment) { - MockProject project = projectEnvironment.getProject(); - project.registerService(CoreJavaFileManager.class, (CoreJavaFileManager) ServiceManager.getService(project, JavaFileManager.class)); - CliLightClassGenerationSupport cliLightClassGenerationSupport = new CliLightClassGenerationSupport(project); - project.registerService(LightClassGenerationSupport.class, cliLightClassGenerationSupport); - project.registerService(CliLightClassGenerationSupport.class, cliLightClassGenerationSupport); - project.registerService(CodeAnalyzerInitializer.class, cliLightClassGenerationSupport); - - ExtensionsArea area = Extensions.getArea(project); - - area.getExtensionPoint(PsiElementFinder.EP_NAME) - .registerExtension(new PsiElementFinderImpl(project, ServiceManager.getService(project, JavaFileManager.class))); - - area.getExtensionPoint(PsiElementFinder.EP_NAME) - .registerExtension(new JavaElementFinder(project, cliLightClassGenerationSupport)); + public fun getProject(): Project { + return projectEnvironment.getProject() } - @NotNull - public CompilerConfiguration getConfiguration() { - return configuration; - } - - @NotNull - private CoreApplicationEnvironment getMyApplicationEnvironment() { - return projectEnvironment.getEnvironment(); - } - - @NotNull - public MockApplication getApplication() { - return getMyApplicationEnvironment().getApplication(); - } - - @NotNull - public Project getProject() { - return projectEnvironment.getProject(); - } - - private void addExternalAnnotationsRoot(File path) { + private fun addExternalAnnotationsRoot(path: File) { if (!path.exists()) { - report(WARNING, "Annotations path entry points to a non-existent location: " + path); - return; + report(WARNING, "Annotations path entry points to a non-existent location: " + path) + return } - annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path)); + annotationsManager.addExternalAnnotationsRoot(PathUtil.jarFileOrDirectoryToVirtualFile(path)) } - private void addToClasspath(File path) { + private fun addToClasspath(path: File) { if (path.isFile()) { - VirtualFile jarFile = getMyApplicationEnvironment().getJarFileSystem().findFileByPath(path + "!/"); + val jarFile = getMyApplicationEnvironment().getJarFileSystem().findFileByPath(path + "!/") if (jarFile == null) { - report(WARNING, "Classpath entry points to a file that is not a JAR archive: " + path); - return; + report(WARNING, "Classpath entry points to a file that is not a JAR archive: " + path) + return } - projectEnvironment.addJarToClassPath(path); - classPath.add(jarFile); + projectEnvironment.addJarToClassPath(path) + classPath.add(jarFile) } else { - VirtualFile root = getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(path.getAbsolutePath()); + val root = getMyApplicationEnvironment().getLocalFileSystem().findFileByPath(path.getAbsolutePath()) if (root == null) { - report(WARNING, "Classpath entry points to a non-existent location: " + path); - return; + report(WARNING, "Classpath entry points to a non-existent location: " + path) + return } - projectEnvironment.addSourcesToClasspath(root); - classPath.add(root); + projectEnvironment.addSourcesToClasspath(root) + classPath.add(root) } } - @NotNull - private Collection getSourceRootsCheckingForDuplicates() { - Set uniqueSourceRoots = Sets.newLinkedHashSet(); + private fun getSourceRootsCheckingForDuplicates(): Collection { + val uniqueSourceRoots = Sets.newLinkedHashSet() - for (String sourceRoot : configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY)) { + for (sourceRoot in configuration.getList(CommonConfigurationKeys.SOURCE_ROOTS_KEY)) { if (!uniqueSourceRoots.add(sourceRoot)) { - report(WARNING, "Duplicate source root: " + sourceRoot); + report(WARNING, "Duplicate source root: " + sourceRoot) } } - return uniqueSourceRoots; + return uniqueSourceRoots } - @NotNull - public List getSourceFiles() { - return sourceFiles; + public fun getSourceFiles(): List { + return sourceFiles } - private void report(@NotNull CompilerMessageSeverity severity, @NotNull String message) { - MessageCollector messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY); + private fun report(severity: CompilerMessageSeverity, message: String) { + val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) if (messageCollector != null) { - messageCollector.report(severity, message, CompilerMessageLocation.NO_LOCATION); + messageCollector.report(severity, message, CompilerMessageLocation.NO_LOCATION) } else { - throw new CompileEnvironmentException(message); + throw CompileEnvironmentException(message) + } + } + + companion object { + + private val APPLICATION_LOCK = Object() + private var ourApplicationEnvironment: JavaCoreApplicationEnvironment? = null + private var ourProjectCount = 0 + + public fun createForProduction(parentDisposable: Disposable, configuration: CompilerConfiguration, configFilePaths: List): JetCoreEnvironment { + // JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ) + // All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well + Disposer.register(parentDisposable, object : Disposable { + override fun dispose() { + synchronized (APPLICATION_LOCK) { + if (--ourProjectCount <= 0) { + disposeApplicationEnvironment() + } + } + } + }) + val environment = JetCoreEnvironment(parentDisposable, getOrCreateApplicationEnvironmentForProduction(configuration, configFilePaths), configuration) + + synchronized (APPLICATION_LOCK) { + ourProjectCount++ + } + return environment + } + + TestOnly + public fun createForTests(parentDisposable: Disposable, configuration: CompilerConfiguration, extensionConfigs: List): JetCoreEnvironment { + // Tests are supposed to create a single project and dispose it right after use + return JetCoreEnvironment(parentDisposable, createApplicationEnvironment(parentDisposable, configuration, extensionConfigs), configuration) + } + + private fun getOrCreateApplicationEnvironmentForProduction(configuration: CompilerConfiguration, configFilePaths: List): JavaCoreApplicationEnvironment { + synchronized (APPLICATION_LOCK) { + if (ourApplicationEnvironment != null) return ourApplicationEnvironment + + val parentDisposable = Disposer.newDisposable() + ourApplicationEnvironment = createApplicationEnvironment(parentDisposable, configuration, configFilePaths) + ourProjectCount = 0 + Disposer.register(parentDisposable, object : Disposable { + override fun dispose() { + synchronized (APPLICATION_LOCK) { + ourApplicationEnvironment = null + } + } + }) + return ourApplicationEnvironment + } + } + + public fun disposeApplicationEnvironment() { + synchronized (APPLICATION_LOCK) { + if (ourApplicationEnvironment == null) return + val environment = ourApplicationEnvironment + ourApplicationEnvironment = null + Disposer.dispose(environment.getParentDisposable()) + } + } + + private fun createApplicationEnvironment(parentDisposable: Disposable, configuration: CompilerConfiguration, configFilePaths: List): JavaCoreApplicationEnvironment { + Extensions.cleanRootArea(parentDisposable) + registerAppExtensionPoints() + val applicationEnvironment = JavaCoreApplicationEnvironment(parentDisposable) + + for (configPath in configFilePaths) { + registerApplicationExtensionPointsAndExtensionsFrom(configuration, configPath) + } + + registerApplicationServicesForCLI(applicationEnvironment) + registerApplicationServices(applicationEnvironment) + + return applicationEnvironment + } + + private fun registerAppExtensionPoints() { + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContentBasedFileSubstitutor.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint>(Extensions.getRootArea(), BinaryFileStubBuilders.EP_NAME, javaClass>()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), FileContextProvider.EP_NAME, javaClass()) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), MetaDataContributor.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsStubBuilderFactory.EP_NAME, javaClass>()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), PsiAugmentProvider.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), JavaMainMethodProvider.EP_NAME, javaClass()) + // + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ContainerProvider.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClsCustomNavigationPolicy.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint(Extensions.getRootArea(), ClassFileDecompilers.EP_NAME, javaClass()) + } + + private fun registerApplicationExtensionPointsAndExtensionsFrom(configuration: CompilerConfiguration, configFilePath: String) { + val locator = configuration.get(JVMConfigurationKeys.COMPILER_JAR_LOCATOR) + var pluginRoot = if (locator == null) PathUtil.getPathUtilJar() else locator.getCompilerJar() + + val app = ApplicationManager.getApplication() + val parentFile = pluginRoot.getParentFile() + + if (pluginRoot.isDirectory() && app != null && app.isUnitTestMode() && FileUtil.toCanonicalPath(parentFile.getPath()).endsWith("out/production")) { + // hack for load extensions when compiler run directly from out directory(e.g. in tests) + val srcDir = parentFile.getParentFile().getParentFile() + pluginRoot = File(srcDir, "idea/src") + } + + CoreApplicationEnvironment.registerExtensionPointAndExtensions(pluginRoot, configFilePath, Extensions.getRootArea()) + } + + private fun registerApplicationServicesForCLI(applicationEnvironment: JavaCoreApplicationEnvironment) { + // ability to get text from annotations xml files + applicationEnvironment.registerFileType(PlainTextFileType.INSTANCE, "xml") + applicationEnvironment.registerParserDefinition(JavaParserDefinition()) + } + + // made public for Upsource + public fun registerApplicationServices(applicationEnvironment: JavaCoreApplicationEnvironment) { + applicationEnvironment.registerFileType(JetFileType.INSTANCE, "kt") + applicationEnvironment.registerFileType(JetFileType.INSTANCE, "ktm") + applicationEnvironment.registerFileType(JetFileType.INSTANCE, JetParserDefinition.STD_SCRIPT_SUFFIX) // should be renamed to kts + applicationEnvironment.registerParserDefinition(JetParserDefinition()) + + applicationEnvironment.getApplication().registerService(javaClass(), KotlinBinaryClassCache()) + } + + private fun registerProjectExtensionPoints(area: ExtensionsArea) { + CoreApplicationEnvironment.registerExtensionPoint(area, PsiTreeChangePreprocessor.EP_NAME, javaClass()) + CoreApplicationEnvironment.registerExtensionPoint(area, PsiElementFinder.EP_NAME, javaClass()) + } + + // made public for Upsource + public fun registerProjectServices(projectEnvironment: JavaCoreProjectEnvironment) { + val project = projectEnvironment.getProject() + project.registerService(javaClass(), JetScriptDefinitionProvider()) + + project.registerService(javaClass(), KotlinJavaPsiFacade(project)) + project.registerService(javaClass(), KotlinLightClassForPackage.FileStubCache(project)) + } + + private fun registerProjectServicesForCLI(projectEnvironment: JavaCoreProjectEnvironment) { + val project = projectEnvironment.getProject() + project.registerService(javaClass(), ServiceManager.getService(project, javaClass()) as CoreJavaFileManager) + val cliLightClassGenerationSupport = CliLightClassGenerationSupport(project) + project.registerService(javaClass(), cliLightClassGenerationSupport) + project.registerService(javaClass(), cliLightClassGenerationSupport) + project.registerService(javaClass(), cliLightClassGenerationSupport) + + val area = Extensions.getArea(project) + + area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(PsiElementFinderImpl(project, ServiceManager.getService(project, javaClass()))) + + area.getExtensionPoint(PsiElementFinder.EP_NAME).registerExtension(JavaElementFinder(project, cliLightClassGenerationSupport)) } } }