LSP-270 Un-hack user data

GitOrigin-RevId: a2d7c8196baefcac898d2169a86721072e6a5c22
This commit is contained in:
Andrey Zaytsev
2025-11-16 13:46:55 +00:00
committed by intellij-monorepo-bot
parent 30c410f055
commit bf42314169
2 changed files with 37 additions and 141 deletions
@@ -2,7 +2,6 @@
package com.intellij.openapi.util;
import com.intellij.ReviseWhenPortedToJDK;
import com.intellij.openapi.util.userData.ExternalUserDataStorage;
import com.intellij.util.containers.VarHandleWrapper;
import com.intellij.util.keyFMap.KeyFMap;
import com.intellij.util.xmlb.annotations.Transient;
@@ -10,26 +9,12 @@ import org.jetbrains.annotations.*;
import java.io.Serializable;
import java.util.Objects;
import java.util.function.Function;
@ReviseWhenPortedToJDK("11") // rewrite to real VarHandles
@Transient
public class UserDataHolderBase implements UserDataHolderEx, Serializable {
private static final Key<KeyFMap> COPYABLE_USER_MAP_KEY = Key.create("COPYABLE_USER_MAP_KEY");
@Nullable
private static Function<@NotNull UserDataHolderBase, @Nullable ExternalUserDataStorage> ourExternalUserDataStorage = null;
@ApiStatus.Internal
public static void setExternalUserDataStorage(@Nullable Function<@NotNull UserDataHolderBase, @Nullable ExternalUserDataStorage> provider) {
ourExternalUserDataStorage = provider;
}
private @Nullable ExternalUserDataStorage externalStorage() {
Function<@NotNull UserDataHolderBase, @Nullable ExternalUserDataStorage> provider = ourExternalUserDataStorage;
return provider == null ? null : provider.apply(this);
}
private volatile @NotNull KeyFMap value = KeyFMap.EMPTY_MAP;
private static final VarHandleWrapper VALUE_HANDLE = VarHandleWrapper.getFactory().create(UserDataHolderBase.class, "value", KeyFMap.class);
@@ -62,43 +47,25 @@ public class UserDataHolderBase implements UserDataHolderEx, Serializable {
@Override
public <T> T getUserData(@NotNull Key<T> key) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
return external.getUserData(this, key);
}
else {
T t = getUserMap().get(key);
if (t == null && key instanceof KeyWithDefaultValue) {
t = putUserDataIfAbsent(key, ((KeyWithDefaultValue<T>)key).getDefaultValue());
}
return t;
T t = getUserMap().get(key);
if (t == null && key instanceof KeyWithDefaultValue) {
t = putUserDataIfAbsent(key, ((KeyWithDefaultValue<T>)key).getDefaultValue());
}
return t;
}
@ApiStatus.Internal
public @NotNull KeyFMap getUserMap() {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
return external.getUserMap(this);
}
else {
return value;
}
return value;
}
@Override
public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
external.putUserData(this, key, value);
}
else {
while (true) {
KeyFMap map = getUserMap();
KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value);
if (newMap == map || changeUserMap(map, newMap)) {
break;
}
while (true) {
KeyFMap map = getUserMap();
KeyFMap newMap = value == null ? map.minus(key) : map.plus(key, value);
if (newMap == map || changeUserMap(map, newMap)) {
break;
}
}
}
@@ -113,88 +80,45 @@ public class UserDataHolderBase implements UserDataHolderEx, Serializable {
}
public <T> void putCopyableUserData(@NotNull Key<T> key, T value) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
while (true) {
KeyFMap oldCopyableMap = getUserData(COPYABLE_USER_MAP_KEY);
KeyFMap newCopyableMap = oldCopyableMap;
if (oldCopyableMap == null) {
if (value == null) {
//nothing
}
else {
newCopyableMap = KeyFMap.EMPTY_MAP.plus(key, value);
}
}
else {
if (value == null) {
newCopyableMap = oldCopyableMap.minus(key);
}
else {
newCopyableMap = oldCopyableMap.plus(key, value);
}
}
if (
oldCopyableMap == newCopyableMap ||
external.compareAndPutUserData(this, COPYABLE_USER_MAP_KEY, oldCopyableMap, newCopyableMap)
) {
break;
}
while (true) {
KeyFMap map = getUserMap();
KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY);
if (copyableMap == null) {
copyableMap = KeyFMap.EMPTY_MAP;
}
}
else {
while (true) {
KeyFMap map = getUserMap();
KeyFMap copyableMap = map.get(COPYABLE_USER_MAP_KEY);
if (copyableMap == null) {
copyableMap = KeyFMap.EMPTY_MAP;
}
KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value);
KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap);
if (newMap == map || changeUserMap(map, newMap)) {
return;
}
KeyFMap newCopyableMap = value == null ? copyableMap.minus(key) : copyableMap.plus(key, value);
KeyFMap newMap = newCopyableMap.isEmpty() ? map.minus(COPYABLE_USER_MAP_KEY) : map.plus(COPYABLE_USER_MAP_KEY, newCopyableMap);
if (newMap == map || changeUserMap(map, newMap)) {
return;
}
}
}
@Override
public <T> boolean replace(@NotNull Key<T> key, @Nullable T oldValue, @Nullable T newValue) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
return external.compareAndPutUserData(this, key, oldValue, newValue);
}
else {
while (true) {
KeyFMap map = getUserMap();
if (map.get(key) != oldValue) {
return false;
}
KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue);
if (newMap == map || changeUserMap(map, newMap)) {
return true;
}
while (true) {
KeyFMap map = getUserMap();
if (map.get(key) != oldValue) {
return false;
}
KeyFMap newMap = newValue == null ? map.minus(key) : map.plus(key, newValue);
if (newMap == map || changeUserMap(map, newMap)) {
return true;
}
}
}
@Override
public @NotNull <T> T putUserDataIfAbsent(final @NotNull Key<T> key, final @NotNull T value) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
return external.putUserDataIfAbsent(this, key, value);
}
else {
while (true) {
KeyFMap map = getUserMap();
T oldValue = map.get(key);
if (oldValue != null) {
return oldValue;
}
KeyFMap newMap = map.plus(key, value);
if (newMap == map || changeUserMap(map, newMap)) {
return value;
}
while (true) {
KeyFMap map = getUserMap();
T oldValue = map.get(key);
if (oldValue != null) {
return oldValue;
}
KeyFMap newMap = map.plus(key, value);
if (newMap == map || changeUserMap(map, newMap)) {
return value;
}
}
}
@@ -213,13 +137,7 @@ public class UserDataHolderBase implements UserDataHolderEx, Serializable {
}
protected void setUserMap(@NotNull KeyFMap map) {
ExternalUserDataStorage external = externalStorage();
if (external != null) {
external.setUserMap(this, map);
}
else {
value = map;
}
value = map;
}
public boolean isUserDataEmpty() {
@@ -1,22 +0,0 @@
package com.intellij.openapi.util.userData
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import com.intellij.util.keyFMap.KeyFMap
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface ExternalUserDataStorage {
fun <T : Any> getUserData(obj: UserDataHolder, key: Key<T>): T?
fun <T : Any> putUserData(obj: UserDataHolder, key: Key<T>, value: T?)
fun <T : Any> compareAndPutUserData(obj: UserDataHolder, key: Key<T>, oldValue: T?, newValue: T?): Boolean
fun getUserMap(obj: UserDataHolder): KeyFMap
fun setUserMap(obj: UserDataHolder, userMap: KeyFMap)
fun <T : Any> putUserDataIfAbsent(obj: UserDataHolder, key: Key<T>, value: T): T
}