From 4c6c6af8673370ab2ce884e4d10523f7b21a4bda Mon Sep 17 00:00:00 2001 From: Pasqual Koschmieder Date: Thu, 9 Oct 2025 16:26:11 +0200 Subject: [PATCH] refactor: replace LazyMemoizingSupplier with StableValue --- .../unsafe/LazyMemoizingSupplier.java | 83 ------------------- .../impl/transform/unsafe/OpConstants.java | 3 +- .../unsafe/UnsafeReplacementDefiner.java | 1 - .../unsafe/UnsafeReplacementDelegate.java | 10 +-- .../unsafe/UnsafeUsageTraceLogger.java | 3 +- .../impl/transform/unsafe/ValueTypeKind.java | 19 ++--- 6 files changed, 16 insertions(+), 103 deletions(-) delete mode 100644 wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/LazyMemoizingSupplier.java diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/LazyMemoizingSupplier.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/LazyMemoizingSupplier.java deleted file mode 100644 index f82dee0b9f..0000000000 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/LazyMemoizingSupplier.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2019-2024 CloudNetService team & contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package eu.cloudnetservice.wrapper.impl.transform.unsafe; - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.util.Objects; -import java.util.function.Supplier; -import lombok.NonNull; - -/** - * Supplier that computes the underlying value only once, lazily. - * - * @param the type of results supplied by this supplier - * @since 4.0 - */ -@Deprecated // in favor of stable values -final class LazyMemoizingSupplier implements Supplier { - - private static final VarHandle WRAPPED_VAR_HANDLE; - - static { - try { - var lookup = MethodHandles.lookup(); - WRAPPED_VAR_HANDLE = lookup.findVarHandle(LazyMemoizingSupplier.class, "wrapped", Object.class); - } catch (NoSuchFieldException | IllegalAccessException exception) { - throw new ExceptionInInitializerError(exception); - } - } - - private T wrapped; // lazily initialized on first access using var handles; the field is not unused - private Supplier delegateSupplier; - - /** - * Constructs a new instance using the given supplier as the wrapped delegate. The given delegate supplier is not - * allowed to return a null value. - * - * @param delegateSupplier the supplier to use to initialize the wrapped value. - * @throws NullPointerException if the given delegate supplier is null. - */ - public LazyMemoizingSupplier(@NonNull Supplier delegateSupplier) { - this.delegateSupplier = delegateSupplier; - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("unchecked") - public @NonNull T get() { - var wrapped = WRAPPED_VAR_HANDLE.getAcquire(this); - if (wrapped != null) { - return (T) wrapped; - } - - synchronized (this) { - wrapped = this.wrapped; // plain access is safe due to locking - if (wrapped != null) { - return (T) wrapped; - } - - var newValue = this.delegateSupplier.get(); - Objects.requireNonNull(newValue, "delegate supplier must not return null"); - this.delegateSupplier = null; // release delegate for GC, no longer needed - WRAPPED_VAR_HANDLE.setRelease(this, newValue); - return newValue; - } - } -} diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/OpConstants.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/OpConstants.java index 6d59963731..6834ba821f 100644 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/OpConstants.java +++ b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/OpConstants.java @@ -29,8 +29,7 @@ final class OpConstants { /** * Supplier of the jvm-static trusted lookup instance, only initialized on the first access. */ - @SuppressWarnings("deprecation") // fine until stable values are available - static final Supplier TRUSTED_LOOKUP = new LazyMemoizingSupplier<>(() -> { + static final Supplier TRUSTED_LOOKUP = StableValue.supplier(() -> { try { var trustedLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); trustedLookupField.setAccessible(true); diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDefiner.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDefiner.java index e52355305b..99f946e384 100644 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDefiner.java +++ b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDefiner.java @@ -44,7 +44,6 @@ final class UnsafeReplacementDefiner { "ArrayOps", "FieldAccessor", "FieldOffsetOps", - "LazyMemoizingSupplier", "MemoryControlOps", "MemoryOps", "OpConstants", diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDelegate.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDelegate.java index 72be87dff3..dab8b793d3 100644 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDelegate.java +++ b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeReplacementDelegate.java @@ -31,6 +31,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import lombok.NonNull; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.VisibleForTesting; @@ -41,9 +42,8 @@ * methods in {@code sun.misc.Unsafe} have a safe replacement within the jdk, use those instead! * * @since 4.0 - * @deprecated should not be used in new user code, use safe replacements like the ffm api or var handles instead. */ -@Deprecated +@ApiStatus.Internal public final class UnsafeReplacementDelegate { // accessor for cleaning up direct byte buffers @@ -55,7 +55,7 @@ public final class UnsafeReplacementDelegate { // accessor for the operating system mx bean private static final Supplier OS_MX_BEAN = - new LazyMemoizingSupplier<>(ManagementFactory::getOperatingSystemMXBean); + StableValue.supplier(ManagementFactory::getOperatingSystemMXBean); // method handle to define a class in any class loader, takes the target class loader as the first argument // method descriptor: ClassLoader.defineClass1(ClassLoader, String, byte[], int, int, ProtectionDomain, String) @@ -76,7 +76,7 @@ private UnsafeReplacementDelegate() { * @return a supplier for a method handle to invoke {@code ClassLoader.defineClass}. */ private static @NonNull Supplier createClassDefineMethodHandleSupplier() { - return new LazyMemoizingSupplier<>(() -> { + return StableValue.supplier(() -> { try { // resolves the method handle for: Class defineClass1(ClassLoader, String, byte[], int, int, ProtectionDomain, String) var lookup = OpConstants.TRUSTED_LOOKUP.get(); @@ -103,7 +103,7 @@ private UnsafeReplacementDelegate() { * @return a supplier that creates a consumer to clean a direct byte buffer. */ private static @NonNull Supplier> createByteBufferCleaner() { - return new LazyMemoizingSupplier<>(() -> { + return StableValue.supplier(() -> { try { var lookup = OpConstants.TRUSTED_LOOKUP.get(); diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeUsageTraceLogger.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeUsageTraceLogger.java index 1ae16c2461..b0aaba5248 100644 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeUsageTraceLogger.java +++ b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/UnsafeUsageTraceLogger.java @@ -26,7 +26,6 @@ * @since 4.0 */ @ApiStatus.Internal -@SuppressWarnings("deprecation") public final class UnsafeUsageTraceLogger { /** @@ -37,7 +36,7 @@ public final class UnsafeUsageTraceLogger { * Stack walker to get the caller of the unsafe replacement method. */ private static final Supplier CALLER_GET_STACK_WALKER = - new LazyMemoizingSupplier<>(() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)); + StableValue.supplier(() -> StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)); private UnsafeUsageTraceLogger() { throw new UnsupportedOperationException(); diff --git a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/ValueTypeKind.java b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/ValueTypeKind.java index 5d8ab40790..1ca7b9ff22 100644 --- a/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/ValueTypeKind.java +++ b/wrapper-jvm/impl/src/main/java/eu/cloudnetservice/wrapper/impl/transform/unsafe/ValueTypeKind.java @@ -32,43 +32,42 @@ * * @since 4.0 */ -@SuppressWarnings("deprecation") // uses LazyMemoizingSupplier enum ValueTypeKind { // CHECKSTYLE.OFF: checkstyle isn't really helping here BYTE( (left, right) -> (byte) left == (byte) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(byte[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(byte[].class)), arr -> MemorySegment.ofArray((byte[]) arr), ValueLayout.JAVA_BYTE), SHORT( (left, right) -> (short) left == (short) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(short[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(short[].class)), arr -> MemorySegment.ofArray((short[]) arr), ValueLayout.JAVA_SHORT), INT( (left, right) -> (int) left == (int) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(int[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(int[].class)), arr -> MemorySegment.ofArray((int[]) arr), ValueLayout.JAVA_INT), LONG( (left, right) -> (long) left == (long) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(long[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(long[].class)), arr -> MemorySegment.ofArray((long[]) arr), ValueLayout.JAVA_LONG), FLOAT( (left, right) -> (float) left == (float) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(float[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(float[].class)), arr -> MemorySegment.ofArray((float[]) arr), ValueLayout.JAVA_FLOAT), DOUBLE( (left, right) -> (double) left == (double) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(double[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(double[].class)), arr -> MemorySegment.ofArray((double[]) arr), ValueLayout.JAVA_DOUBLE), BOOL( (left, right) -> (boolean) left == (boolean) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(boolean[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(boolean[].class)), arr -> { // boolean arrays aren't natively supported, but they are basically byte arrays with 0/1 values var boolArray = (boolean[]) arr; @@ -81,12 +80,12 @@ enum ValueTypeKind { ValueLayout.JAVA_BOOLEAN), CHAR( (left, right) -> (char) left == (char) right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(char[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(char[].class)), arr -> MemorySegment.ofArray((char[]) arr), ValueLayout.JAVA_CHAR), REF( (left, right) -> left == right, - new LazyMemoizingSupplier<>(() -> MethodHandles.arrayElementVarHandle(Object[].class)), + StableValue.supplier(() -> MethodHandles.arrayElementVarHandle(Object[].class)), null, null); // CHECKSTYLE.ON