mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java, highlighting] add standard "jigsaw" modules from the user's JDK for non-modular projects IDEA-259485
GitOrigin-RevId: 8b8646ff890a89af02fff3b8a5532b4f0565ec6f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1e4a9d69f9
commit
914d16b526
@@ -0,0 +1,83 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.execution.vmModules
|
||||
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.CapturingProcessRunner
|
||||
import com.intellij.execution.process.OSProcessHandler
|
||||
import com.intellij.execution.process.ProcessNotCreatedException
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.JavaSdk
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.util.ConcurrencyUtil
|
||||
import com.intellij.util.concurrency.AppExecutorUtil
|
||||
import com.intellij.util.containers.CollectionFactory
|
||||
import java.util.concurrent.*
|
||||
|
||||
class VmModulesExecutor {
|
||||
companion object {
|
||||
private val ourData: ConcurrentMap<String, CompletableFuture<List<String>>> = CollectionFactory.createConcurrentSoftValueMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or computes the list of jigsaw modules available for the specified JDK.
|
||||
*
|
||||
* @param module the intellij module for which to retrieve or compute the list of available jigsaw modules
|
||||
* @return the list of jigsaw modules available for the specified intellij module or
|
||||
* empty if the intellij module does not contain a JDK or an error has occurred.
|
||||
*/
|
||||
fun getOrComputeModulesForJdk(module: Module): List<String> {
|
||||
val sdk = ModuleRootManager.getInstance(module).sdk ?: return listOf()
|
||||
if (sdk.sdkType !is JavaSdk || sdk.homePath == null) return listOf()
|
||||
try {
|
||||
val future = getOrCreateFuture(sdk)
|
||||
if (future.isDone) {
|
||||
return future.get(ConcurrencyUtil.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
catch (_: InterruptedException) {
|
||||
}
|
||||
catch (_: TimeoutException) {
|
||||
}
|
||||
catch (_: ExecutionException) {
|
||||
}
|
||||
return listOf()
|
||||
}
|
||||
|
||||
private fun getOrCreateFuture(sdk: Sdk): CompletableFuture<List<String>> {
|
||||
val sdkHome = sdk.homePath
|
||||
var future = ourData.computeIfAbsent(sdkHome) { CompletableFuture.supplyAsync({ computeModules(sdk) }, AppExecutorUtil.getAppExecutorService()) }
|
||||
if (future.isDone) {
|
||||
// sometimes the timeout may appear, and in order not to block the possibility to get the completion afterwards, it is better to retry
|
||||
if (future.get(ConcurrencyUtil.DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS) == null) {
|
||||
future = ourData.computeIfPresent(sdkHome) { _, value ->
|
||||
if (future != value) return@computeIfPresent value // another thread has already changed the value
|
||||
return@computeIfPresent CompletableFuture.supplyAsync({ computeModules(sdk) }, AppExecutorUtil.getAppExecutorService())
|
||||
}
|
||||
}
|
||||
}
|
||||
return future
|
||||
}
|
||||
|
||||
// when null is returned, it was a timeout
|
||||
private fun computeModules(sdk: Sdk): List<String>? {
|
||||
val vmPath = JavaSdk.getInstance().getVMExecutablePath(sdk)
|
||||
val generalCommandLine = GeneralCommandLine(vmPath).apply {
|
||||
addParameters("--list-modules")
|
||||
}
|
||||
try {
|
||||
val handler = OSProcessHandler(generalCommandLine)
|
||||
val runner = CapturingProcessRunner(handler)
|
||||
val output = runner.runProcess(1_000)
|
||||
if (output.isTimeout) {
|
||||
return null
|
||||
}
|
||||
else {
|
||||
return output.stdout.lineSequence().map { line -> line.substringBefore('@') }.toList()
|
||||
}
|
||||
}
|
||||
catch (e: ProcessNotCreatedException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.AddExportsDirectiveFix
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.AddRequiresDirectiveFix
|
||||
import com.intellij.codeInspection.util.IntentionName
|
||||
import com.intellij.execution.vmModules.VmModulesExecutor
|
||||
import com.intellij.java.JavaBundle
|
||||
import com.intellij.modcommand.ActionContext
|
||||
import com.intellij.modcommand.ModCommand
|
||||
@@ -40,6 +41,8 @@ private val javaVersionPattern: Pattern by lazy { Pattern.compile("java\\d+") }
|
||||
* @see <a href="http://openjdk.org/jeps/261">JEP 261: Module System</a>
|
||||
*/
|
||||
internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
private val vmModulesExecutor: VmModulesExecutor = VmModulesExecutor()
|
||||
|
||||
override fun getName(): String = JavaBundle.message("java.platform.module.system.name")
|
||||
|
||||
override fun isAccessible(targetPackageName: String, targetFile: PsiFile?, place: PsiElement): Boolean {
|
||||
@@ -50,8 +53,8 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
return getProblem(targetPackageName, targetFile, place, false) { use, _, target, _, _ -> JavaModuleGraphUtil.reads(use, target) }
|
||||
}
|
||||
|
||||
private fun isExported(useModule: PsiJavaModule, packageName: String, targetModule: PsiJavaModule, useModuleName: String, module: Module?) : Boolean {
|
||||
if(!targetModule.isPhysical || JavaModuleGraphUtil.exports(targetModule, packageName, useModule)) return true
|
||||
private fun isExported(useModule: PsiJavaModule, packageName: String, targetModule: PsiJavaModule, useModuleName: String, module: Module?): Boolean {
|
||||
if (!targetModule.isPhysical || JavaModuleGraphUtil.exports(targetModule, packageName, useModule)) return true
|
||||
if (module == null) return false
|
||||
return inAddedExports(module, targetModule.name, packageName, useModuleName)
|
||||
}
|
||||
@@ -118,16 +121,12 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
if (targetName.startsWith("java.") &&
|
||||
targetName != PsiJavaModule.JAVA_BASE &&
|
||||
!inAddedModules(module, targetName) &&
|
||||
!hasUpgrade(module, targetName, packageName, place)) {
|
||||
val root = DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode<PsiJavaModule, Throwable> {
|
||||
JavaPsiFacade.getInstance(place.project).findModule("java.se", module.moduleWithLibrariesScope)
|
||||
}
|
||||
if (root != null && !isAccessible(root, packageName, targetModule, useName, module)) {
|
||||
return if (quick) ERR
|
||||
else ErrorWithFixes(
|
||||
JavaErrorBundle.message("module.access.not.in.graph", packageName, targetName),
|
||||
listOf(AddModulesOptionFix(module, targetName).asIntention()))
|
||||
}
|
||||
!hasUpgrade(module, targetName, packageName, place) &&
|
||||
!accessibleFromLoadedModules(module, targetName, place, isAccessible, packageName, targetModule, useName)) {
|
||||
return if (quick) ERR
|
||||
else ErrorWithFixes(
|
||||
JavaErrorBundle.message("module.access.not.in.graph", packageName, targetName),
|
||||
listOf(AddModulesOptionFix(module, targetName).asIntention()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +174,25 @@ internal class JavaPlatformModuleSystem : JavaModuleSystemEx {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun accessibleFromLoadedModules(module: Module,
|
||||
targetName: String,
|
||||
place: PsiFileSystemItem,
|
||||
isAccessible: (useModule: PsiJavaModule, packageName: String, targetModule: PsiJavaModule, useModuleName: String, module: Module?) -> Boolean,
|
||||
packageName: String,
|
||||
targetModule: PsiJavaModule,
|
||||
useName: String): Boolean {
|
||||
val modules = vmModulesExecutor.getOrComputeModulesForJdk(module)
|
||||
if (!modules.isEmpty()) {
|
||||
return modules.contains(targetName)
|
||||
}
|
||||
else {
|
||||
val root = DumbModeAccessType.RELIABLE_DATA_ONLY.ignoreDumbMode<PsiJavaModule, Throwable> {
|
||||
JavaPsiFacade.getInstance(place.project).findModule("java.se", module.moduleWithLibrariesScope)
|
||||
}
|
||||
return root == null || isAccessible(root, packageName, targetModule, useName, module)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inSameMultiReleaseModule(place: PsiElement, target: PsiElement): Boolean {
|
||||
val placeModule = ModuleUtilCore.findModuleForPsiElement(place) ?: return false
|
||||
val targetModule = ModuleUtilCore.findModuleForPsiElement(target) ?: return false
|
||||
|
||||
Reference in New Issue
Block a user