mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-14 09:12:22 +07:00
IJPL-161739: Add safe extensions to UserDataHolderEx.kt and deprecate/mark unsafe APIs
GitOrigin-RevId: 0bf8dc95df07d5a9bdc21ac5715c3ee8cd15feed
This commit is contained in:
committed by
intellij-monorepo-bot
parent
df4b25fc79
commit
0629629994
86
.idea/libraries/jetbrains_kotlinx_lincheck_jvm.xml
generated
Normal file
86
.idea/libraries/jetbrains_kotlinx_lincheck_jvm.xml
generated
Normal file
@@ -0,0 +1,86 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.kotlinx.lincheck.jvm" type="repository">
|
||||
<properties maven-id="org.jetbrains.kotlinx:lincheck-jvm:2.33">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/lincheck-jvm/2.33/lincheck-jvm-2.33.jar">
|
||||
<sha256sum>2dd9c390e2de0acbdd2fce1aabadd378fa75a9cf73ee723dd8cecbe5c8480457</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.21/kotlin-reflect-1.9.21.jar">
|
||||
<sha256sum>a133e049f0a4e249651582428e166de4dfac9546adf436b6172119255ede510f</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.3/kotlinx-coroutines-core-jvm-1.7.3.jar">
|
||||
<sha256sum>1ab3acc38f3e7355c4f9d1ec62107a46fa73c899f3070d055e5d4373dfe67e12</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.6/asm-commons-9.6.jar">
|
||||
<sha256sum>7aefd0d5c0901701c69f7513feda765fb6be33af2ce7aa17c5781fc87657c511</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.6/asm-9.6.jar">
|
||||
<sha256sum>3c6fac2424db3d4a853b669f4e3d1d9c3c552235e19a319673f887083c2303a1</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar">
|
||||
<sha256sum>c43ecf17b539c777e15da7b5b86553b377e2d39a683de6285567d5283888e7ef</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.6/asm-util-9.6.jar">
|
||||
<sha256sum>c635a7402f4aa9bf66b2f4230cea62025a0fe1cd63e8729adefc9b1994fac4c3</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.6/asm-analysis-9.6.jar">
|
||||
<sha256sum>d92832d7c37edc07c60e2559ac6118b31d642e337a6671edcb7ba9fae68edbbb</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.14.12/byte-buddy-1.14.12.jar">
|
||||
<sha256sum>970636134d61c183b19f8f58fa631e30d2f2abca344b37848a393cac7863dd70</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.14.12/byte-buddy-agent-1.14.12.jar">
|
||||
<sha256sum>2b309a9300092e0b696f7c471fd51d9969001df784c8ab9f07997437d757ad6d</sha256sum>
|
||||
</artifact>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/atomicfu-jvm/0.20.2/atomicfu-jvm-0.20.2.jar">
|
||||
<sha256sum>b3d9ec0298e84ed09e0448c52a643bfdad7f3d8096f1303592a3046e711551af</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
<exclude>
|
||||
<dependency maven-id="org.jetbrains.kotlin:kotlin-stdlib" />
|
||||
<dependency maven-id="org.jetbrains.kotlin:kotlin-stdlib-common" />
|
||||
<dependency maven-id="org.jetbrains:annotations" />
|
||||
<dependency maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8" />
|
||||
</exclude>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/lincheck-jvm/2.33/lincheck-jvm-2.33.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.21/kotlin-reflect-1.9.21.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.3/kotlinx-coroutines-core-jvm-1.7.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.6/asm-commons-9.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.6/asm-9.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.6/asm-tree-9.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.6/asm-util-9.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.6/asm-analysis-9.6.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.14.12/byte-buddy-1.14.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.14.12/byte-buddy-agent-1.14.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/atomicfu-jvm/0.20.2/atomicfu-jvm-0.20.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/lincheck-jvm/2.33/lincheck-jvm-2.33-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.21/kotlin-reflect-1.9.21-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.3/kotlinx-coroutines-core-jvm-1.7.3-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.6/asm-commons-9.6-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.6/asm-9.6-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.6/asm-tree-9.6-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.6/asm-util-9.6-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.6/asm-analysis-9.6-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.14.12/byte-buddy-1.14.12-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.14.12/byte-buddy-agent-1.14.12-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/atomicfu-jvm/0.20.2/atomicfu-jvm-0.20.2-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/lincheck-jvm/2.33/lincheck-jvm-2.33-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.21/kotlin-reflect-1.9.21-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.7.3/kotlinx-coroutines-core-jvm-1.7.3-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.6/asm-commons-9.6-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.6/asm-9.6-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.6/asm-tree-9.6-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.6/asm-util-9.6-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.6/asm-analysis-9.6-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.14.12/byte-buddy-1.14.12-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.14.12/byte-buddy-agent-1.14.12-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/atomicfu-jvm/0.20.2/atomicfu-jvm-0.20.2-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -1101,8 +1101,14 @@ com.intellij.openapi.util.UserDataHolderEx
|
||||
- a:putUserDataIfAbsent(com.intellij.openapi.util.Key,java.lang.Object):java.lang.Object
|
||||
- a:replace(com.intellij.openapi.util.Key,java.lang.Object,java.lang.Object):Z
|
||||
f:com.intellij.openapi.util.UserDataHolderExKt
|
||||
- sf:getAndUpdateUserData(com.intellij.openapi.util.UserDataHolderEx,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function1):java.lang.Object
|
||||
- sf:getOrCreateUserData(com.intellij.openapi.util.UserDataHolder,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:getOrCreateUserData(com.intellij.openapi.util.UserDataHolderEx,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:getOrCreateUserDataUnsafe(com.intellij.openapi.util.UserDataHolder,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:getOrMaybeCreateUserData(com.intellij.openapi.util.UserDataHolderEx,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:nullableLazyValue(com.intellij.openapi.util.UserDataHolder,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:nullableLazyValueUnsafe(com.intellij.openapi.util.UserDataHolder,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function0):java.lang.Object
|
||||
- sf:updateUserData(com.intellij.openapi.util.UserDataHolderEx,com.intellij.openapi.util.Key,kotlin.jvm.functions.Function1):java.lang.Object
|
||||
com.intellij.openapi.util.ValueKey
|
||||
- sf:Companion:com.intellij.openapi.util.ValueKey$Companion
|
||||
- a:getName():java.lang.String
|
||||
|
||||
@@ -3,7 +3,68 @@ package com.intellij.openapi.util
|
||||
|
||||
import com.intellij.util.ObjectUtils
|
||||
|
||||
inline fun <T> UserDataHolder.getOrCreateUserData(key: Key<T>, producer: () -> T): T {
|
||||
/**
|
||||
* Get or create the user-data under the current key.
|
||||
* This operation is atomic as it relies on the atomic [UserDataHolderEx.putUserDataIfAbsent]
|
||||
*/
|
||||
inline fun <T : Any> UserDataHolderEx.getOrCreateUserData(key: Key<T>, producer: () -> T): T {
|
||||
getUserData(key)?.let { return it }
|
||||
return putUserDataIfAbsent(key, producer())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the user-data under the current key.
|
||||
* @param producer Tries to create the value of returns `null` if the value cannot be created.
|
||||
* This operation is atomic as it relies on the atomic [UserDataHolderEx.putUserDataIfAbsent]
|
||||
*/
|
||||
inline fun <T> UserDataHolderEx.getOrMaybeCreateUserData(key: Key<T>, producer: () -> T?): T? {
|
||||
getUserData(key)?.let { return it }
|
||||
val newValueOrNull = producer() ?: return null
|
||||
return putUserDataIfAbsent(key, newValueOrNull)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user data based upon the current value.
|
||||
* Note: This method is considered to be thread safe as it relies on the atomic [UserDataHolderEx.replace].
|
||||
* Note: The [update] function could be called multiple times when many threads are trying to update the value at the same time.
|
||||
* @return The updated value (null if the update function returned null)
|
||||
*/
|
||||
inline fun <T : Any> UserDataHolderEx.updateUserData(key: Key<T>, update: (T?) -> T?): T? {
|
||||
while (true) {
|
||||
val existing = getUserData(key)
|
||||
val newValue = update(existing)
|
||||
if (replace(key, existing, newValue)) {
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user data based upon the current value.
|
||||
* Note: This method is considered to be thread safe as it relies on the atomic [UserDataHolderEx.replace].
|
||||
* Note: The [update] function could be called multiple times when many threads are trying to update the value at the same time.
|
||||
* @return The previous value, which got replaced by the update
|
||||
*/
|
||||
inline fun <T : Any> UserDataHolderEx.getAndUpdateUserData(key: Key<T>, update: (T?) -> T?): T? {
|
||||
while (true) {
|
||||
val existing = getUserData(key)
|
||||
val newValue = update(existing)
|
||||
if (replace(key, existing, newValue)) {
|
||||
return existing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
UNSAFE / DEPRECATED APIs
|
||||
*/
|
||||
|
||||
/**
|
||||
* Warning: This method is not thread-safe: Use [UserDataHolderEx] based APIs instead
|
||||
* See: [UserDataHolderEx.getOrCreateUserData]
|
||||
*/
|
||||
inline fun <T> UserDataHolder.getOrCreateUserDataUnsafe(key: Key<T>, producer: () -> T): T {
|
||||
val existing = getUserData(key)
|
||||
if (existing != null) return existing
|
||||
|
||||
@@ -12,7 +73,19 @@ inline fun <T> UserDataHolder.getOrCreateUserData(key: Key<T>, producer: () -> T
|
||||
return value
|
||||
}
|
||||
|
||||
inline fun <T> UserDataHolder.nullableLazyValue(key: Key<T>, producer: () -> T?): T? {
|
||||
/**
|
||||
* Warning: This method is not thread-safe: Use [UserDataHolderEx] based APIs instead
|
||||
* See: [UserDataHolderEx.getOrCreateUserData]
|
||||
*/
|
||||
@Deprecated("Use 'UserDataHolderEx' APIs instead", replaceWith = ReplaceWith("getOrCreateUserDataUnsafe(key, producer)"))
|
||||
inline fun <T> UserDataHolder.getOrCreateUserData(key: Key<T>, producer: () -> T): T {
|
||||
return getOrCreateUserDataUnsafe(key, producer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method is not thread safe
|
||||
*/
|
||||
inline fun <T> UserDataHolder.nullableLazyValueUnsafe(key: Key<T>, producer: () -> T?): T? {
|
||||
val existing = getUserData(key)
|
||||
if (existing == ObjectUtils.NULL) return null
|
||||
if (existing != null) return existing
|
||||
@@ -22,3 +95,12 @@ inline fun <T> UserDataHolder.nullableLazyValue(key: Key<T>, producer: () -> T?)
|
||||
putUserData(key, value ?: ObjectUtils.NULL as T)
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Warning: This method is not thread-safe: Use [UserDataHolderEx] based APIs instead
|
||||
*/
|
||||
@Deprecated("Use 'UserDataHolderEx' APIs instead", replaceWith = ReplaceWith("nullableLazyValueUnsafe(key, producer)"))
|
||||
inline fun <T> UserDataHolder.nullableLazyValue(key: Key<T>, producer: () -> T?): T? {
|
||||
return nullableLazyValueUnsafe(key, producer)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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.openapi.util
|
||||
|
||||
import org.jetbrains.kotlinx.lincheck.annotations.Operation
|
||||
import org.jetbrains.kotlinx.lincheck.check
|
||||
import org.jetbrains.kotlinx.lincheck.strategy.stress.StressOptions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("unused")
|
||||
class UserDataHolderExLincheck {
|
||||
|
||||
companion object {
|
||||
private val keyA = Key<Int>("a")
|
||||
private val keyB = Key<Int>("b")
|
||||
}
|
||||
|
||||
val holder = UserDataHolderBase()
|
||||
|
||||
@Operation
|
||||
fun getOrCreateA(value: Int) = holder.getOrCreateUserData(keyA) { value }
|
||||
|
||||
@Operation
|
||||
fun setKeyA(value: Int?) = holder.putUserData(keyA, value)
|
||||
|
||||
@Operation
|
||||
fun updateKeyA(value: Int) = holder.updateUserData(keyA) { value }
|
||||
|
||||
@Operation
|
||||
fun getAndUpdateKeyA(value: Int) = holder.getAndUpdateUserData(keyA) { value }
|
||||
|
||||
@Operation
|
||||
fun incrementKeyA(value: Int) = holder.updateUserData(keyA) { (it ?: 0) + value }
|
||||
|
||||
@Operation
|
||||
fun getOrCreateB(value: Int) = holder.getOrCreateUserData(keyB) { value }
|
||||
|
||||
@Operation
|
||||
fun setKeyB(value: Int?) = holder.putUserData(keyB, value)
|
||||
|
||||
@Operation
|
||||
fun updateKeyB(value: Int) = holder.updateUserData(keyB) { value }
|
||||
|
||||
@Operation
|
||||
fun getAndUpdateKeyB(value: Int) = holder.getAndUpdateUserData(keyB) { value }
|
||||
|
||||
@Operation
|
||||
fun incrementKeyB(value: Int) = holder.updateUserData(keyB) { (it ?: 0) + value }
|
||||
|
||||
@Test
|
||||
fun stressTest() = StressOptions().check(this::class)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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.openapi.util
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class UserDataHolderExTest {
|
||||
|
||||
@Test
|
||||
fun getOrCreateUserData() {
|
||||
val key = Key<Int>("a")
|
||||
val userData = UserDataHolderBase()
|
||||
assertEquals(42, userData.getOrCreateUserData(key) { 42 })
|
||||
assertEquals(42, userData.getOrCreateUserData(key) { 239 })
|
||||
|
||||
userData.putUserData(key, null)
|
||||
assertEquals(2411, userData.getOrCreateUserData(key, { 2411 }))
|
||||
assertEquals(2411, userData.getUserData(key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getOrCreateUserDataNullable() {
|
||||
val key = Key<Int>("a")
|
||||
val userData = UserDataHolderBase()
|
||||
assertEquals(null, userData.getOrMaybeCreateUserData(key) { null })
|
||||
assertEquals(42, userData.getOrCreateUserData(key) { 42 })
|
||||
assertEquals(42, userData.getOrMaybeCreateUserData(key) { null })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateUserData() {
|
||||
val key = Key<Int>("a")
|
||||
val userData = UserDataHolderBase()
|
||||
userData.updateUserData(key) { 1 }
|
||||
assertEquals(1, userData.getUserData(key))
|
||||
|
||||
assertEquals(2, userData.updateUserData(key) { value ->
|
||||
(value ?: 0).plus(1)
|
||||
})
|
||||
|
||||
assertEquals(null, userData.updateUserData(key) { null })
|
||||
assertEquals(null, userData.getUserData(key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getAndUpdateUserData() {
|
||||
val key = Key<Int>("a")
|
||||
val userData = UserDataHolderBase()
|
||||
assertEquals(null, userData.getAndUpdateUserData(key) { 1 })
|
||||
assertEquals(1, userData.getAndUpdateUserData(key) { 2 })
|
||||
assertEquals(2, userData.getUserData(key))
|
||||
assertEquals(2, userData.getAndUpdateUserData(key) { it!! + 1 })
|
||||
assertEquals(3, userData.getUserData(key))
|
||||
assertEquals(3, userData.getAndUpdateUserData(key) { null })
|
||||
assertEquals(null, userData.getUserData(key))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,5 +35,6 @@
|
||||
<orderEntry type="library" name="kotlinx-coroutines-debug" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.tools.ide.metrics.benchmark" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" scope="TEST" />
|
||||
<orderEntry type="library" scope="TEST" name="jetbrains.kotlinx.lincheck.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
Reference in New Issue
Block a user