[threading] IJPL-179707: Added compose compiler dependency

GitOrigin-RevId: 10e5d3a83036bf64046af77c3fb6669c59e7e0ab
This commit is contained in:
Moncef Slimani
2025-11-17 13:31:32 +00:00
committed by intellij-monorepo-bot
parent 08fe7ab2bc
commit 3eabd0ac8a
6 changed files with 146 additions and 33 deletions
@@ -8,13 +8,15 @@
<module name="intellij.properties.backend.psi"/>
<module name="intellij.libraries.xerces"/>
<module name="intellij.platform.jewel.foundation"/>
<module name="intellij.platform.jewel.ui"/>
<module name="intellij.platform.jewel.ideLafBridge"/>
<module name="intellij.libraries.compose.foundation.desktop"/>
<module name="intellij.libraries.skiko"/>
<module name="intellij.platform.jewel.markdown.core"/>
<module name="intellij.platform.jewel.markdown.ideLafBridgeStyling"/>
intellijPlatform {
bundledModule("intellij.platform.jewel.foundation")
bundledModule("intellij.platform.jewel.ui")
bundledModule("intellij.platform.jewel.ideLafBridge")
bundledModule("intellij.platform.jewel.markdown.core")
bundledModule("intellij.platform.jewel.markdown.ideLafBridgeStyling")
bundledModule("intellij.libraries.compose.foundation.desktop")
bundledModule("intellij.libraries.skiko")
}
</dependencies>
<resource-bundle>messages.DevKitBundle</resource-bundle>
@@ -740,8 +742,8 @@
<generatedSourcesFilter implementation="org.jetbrains.idea.devkit.contentReport.ContentReportGeneratedSourcesFilter"/>
<toolWindow
id="LockReqsToolWindow"
factoryClass="org.jetbrains.idea.devkit.threadingModelHelper.LockReqsToolWindowFactory"
id="Locking Requirements"
factoryClass="org.jetbrains.idea.devkit.threadingModelHelper.LockReqsToolWindowFactoryJewel"
anchor="bottom"/>
<!-- INTERNAL -->
@@ -15,9 +15,10 @@ class LockReqsAnalyzer {
private const val REQUIRES_READ_LOCK_ANNOTATION = "com.intellij.util.concurrency.annotations.RequiresReadLock"
private const val MAX_PATH_DEPTH = 1000
private val ASYNC_METHODS = setOf("invokeLater", "invokeAndWait", "runInEdt")
enum class LockCheckType {
ANNOTATION,
ASSERTION
ANNOTATION, ASSERTION
}
data class LockRequirement(
@@ -32,12 +33,13 @@ class LockReqsAnalyzer {
val pathString: String
get() = buildString {
append(methodChain.joinToString(" -> ") {
"${it.containingClass?.name}.${it.name}" +
when (lockRequirement.type) {
LockCheckType.ANNOTATION -> "@RequiresReadLock"
LockCheckType.ASSERTION -> "ThreadingAssertions.assertReadAccess()"
}
"${it.containingClass?.name}.${it.name}"
})
append(" -> ")
when (lockRequirement.type) {
LockCheckType.ANNOTATION -> append("@RequiresReadLock")
LockCheckType.ASSERTION -> append("ThreadingAssertions.assertReadAccess()")
}
}
}
}
@@ -64,10 +66,18 @@ class LockReqsAnalyzer {
findLockChecks(method).forEach { check ->
paths.add(ExecutionPath(currentPath.toList(), LockRequirement(check, method)))
}
getMethodCallees(method).forEach { callee -> processMethodDFS(callee, currentPath, paths) }
getMethodCallees(method).forEach {
if (!isAsyncMethod(it)) {
processMethodDFS(it, currentPath, paths)
}
}
currentPath.removeAt(currentPath.lastIndex)
}
private fun isAsyncMethod(method: PsiMethod): Boolean {
return method.name in ASYNC_METHODS
}
private fun getMethodCallees(method: PsiMethod): List<PsiMethod> {
val callees = mutableListOf<PsiMethod>()
method.body?.accept(object : JavaRecursiveElementVisitor() {
@@ -96,8 +106,7 @@ class LockReqsAnalyzer {
}
private fun isAssertReadAccess(expression: PsiMethodCallExpression): Boolean {
return ASSERT_READ_ACCESS_METHOD == expression.methodExpression.referenceName &&
THREADING_ASSERTIONS_CLASS == expression.resolveMethod()?.containingClass?.qualifiedName
return ASSERT_READ_ACCESS_METHOD == expression.methodExpression.referenceName && THREADING_ASSERTIONS_CLASS == expression.resolveMethod()?.containingClass?.qualifiedName
}
private fun hasAssertReadAccessCall(method: PsiMethod): Boolean {
@@ -0,0 +1,47 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.threadingModelHelper
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import java.util.Locale
import org.jetbrains.jewel.bridge.LocalComponent
import org.jetbrains.jewel.foundation.modifier.trackActivation
import org.jetbrains.jewel.foundation.modifier.trackComponentActivation
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.Orientation
import org.jetbrains.jewel.ui.component.Divider
import org.jetbrains.jewel.ui.component.Text
import org.jetbrains.jewel.ui.component.TextArea
import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer
import org.jetbrains.jewel.ui.component.scrollbarContentSafePadding
import com.intellij.openapi.project.Project
@Composable
fun LockReqsCompose(project: Project) {
Column() {
Text("Working now?")
}
}
@@ -8,14 +8,23 @@ import com.intellij.psi.PsiMethod
@Service(Service.Level.PROJECT)
class LockReqsService(private val project: Project) {
private var currentResults: List<String> = emptyList()
companion object {
const val TOOL_WINDOW_ID: String = "Locking Requirements"
}
private var _currentResults: List<String> = emptyList()
val currentResults: List<String>
get() = _currentResults
var onResultsUpdated: (() -> Unit)? = null
fun updateResults(method: PsiMethod) {
val analyzer = LockReqsAnalyzer()
val paths = analyzer.analyzeMethod(method).map { it.pathString }
currentResults = paths
ToolWindowManager.getInstance(project).getToolWindow("LockReqsToolWindow")?.show()
}
val paths = analyzer.analyzeMethod(method)
_currentResults = paths.map { it.pathString }
onResultsUpdated?.invoke()
fun getCurrentResults(): List<String> = currentResults
}
ToolWindowManager.getInstance(project).getToolWindow(TOOL_WINDOW_ID)?.show()
}
}
@@ -1,20 +1,36 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.idea.devkit.threadingModelHelper
import androidx.compose.runtime.Composable
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.ToolWindow
import com.intellij.openapi.wm.ToolWindowFactory
import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.jewel.bridge.addComposeTab
import org.jetbrains.jewel.ui.component.Text
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.table.JBTable
import javax.swing.JPanel
import javax.swing.table.DefaultTableModel
import java.awt.BorderLayout
class LockReqsToolWindowFactory : ToolWindowFactory {
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val service = project.service<LockReqsService>()
toolWindow.addComposeTab(DevKitBundle.message("tab.title.locking.requirements")) {
LockReqsToolWindow(service)
}
val tableModel = DefaultTableModel(arrayOf("Execution Path"), 0)
val panel = JPanel(BorderLayout())
val table = JBTable(tableModel)
val scrollPane = JBScrollPane(table)
panel.add(scrollPane)
service.onResultsUpdated = { loadData(service.currentResults, tableModel) }
val factory = toolWindow.contentManager.factory
val content = factory.createContent(panel, null, false)
toolWindow.contentManager.addContent(content)
}
private fun loadData(results: List<String>, tableModel: DefaultTableModel) {
tableModel.rowCount = 0
results.forEach { tableModel.addRow(arrayOf(it)) }
}
}
@@ -0,0 +1,30 @@
package org.jetbrains.idea.devkit.threadingModelHelper;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.concurrency.annotations.RequiresReadLock;
import testutils.ExpectedPath;
@ExpectedPath("RealWorldUI.testMethod -> Helper.helperMethod -> Service.serviceMethod -> @RequiresReadLock")
@ExpectedPath("RealWorldUI.testMethod -> Helper.helperMethod -> Service.serviceMethod -> ThreadingAssertions.assertReadAccess()")
class RealWorldInsideUI {
void testMethod() {
Helper helper = new Helper();
helper.helperMethod();
}
class Helper {
void helperMethod() {
Service service = new Service();
service.serviceMethod();
}
}
class Service {
@RequiresReadLock
void serviceMethod() {
ThreadingAssertions.assertReadAccess();
}
}
}