[testFramework] IJPL-200805: Do not use Unsafe for checking if a class is initialized

`Unsafe` is removed in JDK 25

GitOrigin-RevId: d726e9b3b5a8e92971dcd6f47264543c1521a9d7
This commit is contained in:
Konstantin Nisht
2025-08-07 14:51:18 +00:00
committed by intellij-monorepo-bot
parent 0e9ed14f70
commit 98a89917c8
4 changed files with 67 additions and 9 deletions
@@ -13,8 +13,10 @@ import com.intellij.util.containers.HashingStrategy;
import com.intellij.util.containers.RefValueHashMapUtil;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
@@ -39,17 +41,17 @@ public final class DebugReflectionUtil {
}));
private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
private static final Method Unsafe_shouldBeInitialized;
private static final Method ClassLoader_findLoadedClass;
static {
Method shouldBeInitialized;
Method findLoadedClass;
try {
shouldBeInitialized = ReflectionUtil.getDeclaredMethod(Class.forName("sun.misc.Unsafe"), "shouldBeInitialized", Class.class);
findLoadedClass = ReflectionUtil.getDeclaredMethod(Class.forName("java.lang.ClassLoader"), "findLoadedClass", String.class);
}
catch (ClassNotFoundException ignored) {
shouldBeInitialized = null;
findLoadedClass = null;
}
Unsafe_shouldBeInitialized = shouldBeInitialized;
ClassLoader_findLoadedClass = findLoadedClass;
}
private static Field @NotNull [] getAllFields(@NotNull Class<?> aClass) {
@@ -98,11 +100,12 @@ public final class DebugReflectionUtil {
return type.isPrimitive() || type == String.class || type == Class.class || type.isArray() && isTrivial(type.getComponentType());
}
private static boolean isInitialized(@NotNull Class<?> root) {
if (Unsafe_shouldBeInitialized == null) return false;
@VisibleForTesting
@ApiStatus.Internal
public static boolean isInitialized(ClassLoader classLoader, @NotNull String rootName) {
boolean isInitialized = false;
try {
isInitialized = !(Boolean)Unsafe_shouldBeInitialized.invoke(ReflectionUtil.getUnsafe(), root);
isInitialized = ClassLoader_findLoadedClass.invoke(classLoader, rootName) != null;
}
catch (Exception e) {
//noinspection CallToPrintStackTrace
@@ -199,7 +202,7 @@ public final class DebugReflectionUtil {
}
}
// check for objects leaking via static fields. process initialized classes only
if (root instanceof Class && isInitialized((Class<?>)root)) {
if (root instanceof Class && isInitialized(((Class<?>)root).getClassLoader(), ((Class<?>)root).getName())) {
for (Field field : getAllFields((Class<?>)root)) {
if ((field.getModifiers() & Modifier.STATIC) == 0) continue;
try {
@@ -0,0 +1,35 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util
import com.intellij.testFramework.junit5.TestApplication
import com.intellij.util.ref.DebugReflectionUtil
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
@TestApplication
class DebugReflectionUtilTest {
@Test
fun `isInitialized returns false for not initialized class`() {
assertFalse {
DebugReflectionUtil.isInitialized(Thread.currentThread().contextClassLoader, "com.intellij.util.ThisClassWillFailOnInitialization")
}
assertThrows<ExceptionInInitializerError> {
ThisClassWillFailOnInitialization()
}
}
@Test
fun `isInitialized returns false for not initialized class 2`() {
assertFalse {
DebugReflectionUtil.isInitialized(Thread.currentThread().contextClassLoader, "com.intellij.util.ThisClassWillNotFailOnInitialization")
}
ThisClassWillNotFailOnInitialization()
assertTrue {
DebugReflectionUtil.isInitialized(Thread.currentThread().contextClassLoader, "com.intellij.util.ThisClassWillNotFailOnInitialization")
}
}
}
@@ -0,0 +1,15 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util;
import kotlin.jvm.JvmStatic;
public class ThisClassWillFailOnInitialization {
static {
willFail();
}
private static void willFail() {
throw new RuntimeException("This class will fail on initialization");
}
}
@@ -0,0 +1,5 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util;
public class ThisClassWillNotFailOnInitialization {
}