From 98a89917c8fb33972b0a6e3a03577fea7a4cb4f2 Mon Sep 17 00:00:00 2001 From: Konstantin Nisht Date: Thu, 7 Aug 2025 13:28:38 +0200 Subject: [PATCH] [testFramework] IJPL-200805: Do not use `Unsafe` for checking if a class is initialized `Unsafe` is removed in JDK 25 GitOrigin-RevId: d726e9b3b5a8e92971dcd6f47264543c1521a9d7 --- .../util/ref/DebugReflectionUtil.java | 21 ++++++----- .../intellij/util/DebugReflectionUtilTest.kt | 35 +++++++++++++++++++ .../ThisClassWillFailOnInitialization.java | 15 ++++++++ .../ThisClassWillNotFailOnInitialization.java | 5 +++ 4 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 platform/platform-tests/testSrc/com/intellij/util/DebugReflectionUtilTest.kt create mode 100644 platform/platform-tests/testSrc/com/intellij/util/ThisClassWillFailOnInitialization.java create mode 100644 platform/platform-tests/testSrc/com/intellij/util/ThisClassWillNotFailOnInitialization.java diff --git a/platform/core-impl/src/com/intellij/util/ref/DebugReflectionUtil.java b/platform/core-impl/src/com/intellij/util/ref/DebugReflectionUtil.java index 7358f37607b7..daf41d0af4bb 100644 --- a/platform/core-impl/src/com/intellij/util/ref/DebugReflectionUtil.java +++ b/platform/core-impl/src/com/intellij/util/ref/DebugReflectionUtil.java @@ -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 { diff --git a/platform/platform-tests/testSrc/com/intellij/util/DebugReflectionUtilTest.kt b/platform/platform-tests/testSrc/com/intellij/util/DebugReflectionUtilTest.kt new file mode 100644 index 000000000000..f75219ef4585 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/util/DebugReflectionUtilTest.kt @@ -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 { + 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") + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillFailOnInitialization.java b/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillFailOnInitialization.java new file mode 100644 index 000000000000..0ccb766068b9 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillFailOnInitialization.java @@ -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"); + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillNotFailOnInitialization.java b/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillNotFailOnInitialization.java new file mode 100644 index 000000000000..80c30156f8a9 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/util/ThisClassWillNotFailOnInitialization.java @@ -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 { +}