mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[threading] IJPL-179707: Added compose compiler dependency
GitOrigin-RevId: 10e5d3a83036bf64046af77c3fb6669c59e7e0ab
This commit is contained in:
committed by
intellij-monorepo-bot
parent
08fe7ab2bc
commit
3eabd0ac8a
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user