This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new b7ba750296 refactor: replace sun.reflect.Reflection with StackWalker 
in Reflect (#3151)
b7ba750296 is described below

commit b7ba75029662a77933ce82f3a122e4040763a37a
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Tue Jun 23 19:32:59 2026 +0800

    refactor: replace sun.reflect.Reflection with StackWalker in Reflect (#3151)
    
    * refactor: replace sun.reflect.Reflection with StackWalker in Reflect
    
    Motivation:
    Reflect.getCallerClass uses sun.reflect.Reflection.getCallerClass via
    reflection, a JDK 8 internal API that was moved to
    jdk.internal.reflect.Reflection in JDK 9+. On JDK 17+ without
    --add-opens for jdk.internal.reflect, this always falls back to None.
    
    Modification:
    Replace with java.lang.StackWalker (JDK 9) with RETAIN_CLASS_REFERENCE
    option, which is the official supported API for accessing caller
    class information. The API contract is preserved: given a frame depth
    index, returns the Class at that depth (or null if beyond stack).
    
    Result:
    Official JDK API instead of internal reflection hack. StackWalker
    was already established in the codebase (LoggerClass.scala,
    TestKitUtils.scala). The findClassLoader fallback now works reliably
    on JDK 17+.
    
    Tests:
    sbt "actor/compile" — passed
    
    References:
    Refs #3136
    
    * test: add directional tests for Reflect.getCallerClass and findClassLoader
    
    Motivation:
    The main PR replaced `sun.reflect.Reflection.getCallerClass` (a JDK 8
    internal API that returns `None` on JDK 17 because
    `Class.forName("sun.reflect.Reflection")` no longer resolves) with the
    official `java.lang.StackWalker` API. The previous spec had no
    coverage of `getCallerClass` itself, so a silent regression — for
    example reverting the implementation back to the `sun.reflect` path —
    would not be caught.
    
    Modification:
    Add four tests to `ReflectSpec`:
    
    - `Reflect.getCallerClass` is defined on JDK 17+ (the old
      implementation always returned `None`; this is the directional test
      that fails on the pre-PR code).
    - `getCaller(0)` returns a non-null `Class` whose name starts with
      `org.apache.pekko.util.Reflect`, validating the frame-skip
      semantics.
    - `getCaller(Int.MaxValue)` returns `null` for a depth beyond the
      stack, matching the `findCaller` contract that filters `ne null`.
    - `Reflect.findClassLoader()` returns a non-null `ClassLoader` for
      the end-to-end path.
    
    Result:
    StackWalker migration is now protected by a directional test that
    would fail on the pre-PR implementation.
    
    Tests:
    sbt "actor-tests/Test/testOnly org.apache.pekko.util.ReflectSpec" --
    10/10 passed (4 new + 6 pre-existing findConstructor tests).
    
    References:
    Discovered during internal review of PR #3151.
    
    * refactor: use imported StackWalker instead of fully-qualified 
java.lang.StackWalker
    
    Motivation:
    Code review feedback to simplify the code by importing StackWalker
    instead of using fully-qualified java.lang.StackWalker references.
    
    Modification:
    Add import for java.lang.StackWalker and replace all fully-qualified
    references with the short name.
    
    Result:
    Cleaner, more readable code with standard Scala import conventions.
    
    References:
    Refs #3151
---
 .../scala/org/apache/pekko/util/ReflectSpec.scala  | 28 ++++++++++++++++++++++
 .../main/scala/org/apache/pekko/util/Reflect.scala | 11 +++++----
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/actor-tests/src/test/scala/org/apache/pekko/util/ReflectSpec.scala 
b/actor-tests/src/test/scala/org/apache/pekko/util/ReflectSpec.scala
index d3b51763c3..d786578cef 100644
--- a/actor-tests/src/test/scala/org/apache/pekko/util/ReflectSpec.scala
+++ b/actor-tests/src/test/scala/org/apache/pekko/util/ReflectSpec.scala
@@ -64,4 +64,32 @@ class ReflectSpec extends AnyWordSpec with Matchers {
     }
   }
 
+  "Reflect#getCallerClass" must {
+
+    "be defined on JDK 17+ (StackWalker is always available)" in {
+      Reflect.getCallerClass shouldBe defined
+    }
+
+    "return a non-null class for a valid stack depth" in {
+      val getCaller = Reflect.getCallerClass.get
+      // Frame 0 is the lambda inside Reflect$ calling StackWalker.walk
+      val frame0 = getCaller(0)
+      frame0 should not be null
+      frame0.getName should startWith("org.apache.pekko.util.Reflect")
+    }
+
+    "return null for index beyond stack depth" in {
+      val getCaller = Reflect.getCallerClass.get
+      getCaller(Int.MaxValue) shouldBe null
+    }
+  }
+
+  "Reflect#findClassLoader" must {
+
+    "return a non-null ClassLoader" in {
+      val cl = Reflect.findClassLoader()
+      cl should not be null
+    }
+  }
+
 }
diff --git a/actor/src/main/scala/org/apache/pekko/util/Reflect.scala 
b/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
index 14d7850590..45203e718b 100644
--- a/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/Reflect.scala
@@ -12,6 +12,7 @@
  */
 
 package org.apache.pekko.util
+import java.lang.StackWalker
 import java.lang.reflect.Constructor
 import java.lang.reflect.ParameterizedType
 import java.lang.reflect.Type
@@ -36,15 +37,17 @@ private[pekko] object Reflect {
    * This optionally holds a function which looks N levels above itself
    * on the call stack and returns the `Class[_]` object for the code
    * executing in that stack frame. Implemented using
-   * `sun.reflect.Reflection.getCallerClass` if available, None otherwise.
+   * `java.lang.StackWalker` (JDK 9+).
    *
    * Hint: when comparing to Thread.currentThread().getStackTrace, add two 
levels.
    */
   val getCallerClass: Option[Int => Class[?]] = {
     try {
-      val c = Class.forName("sun.reflect.Reflection")
-      val m = c.getMethod("getCallerClass", Array(classOf[Int]): _*)
-      Some((i: Int) => m.invoke(null, 
Array[AnyRef](i.asInstanceOf[java.lang.Integer]): _*).asInstanceOf[Class[?]])
+      val walker = StackWalker.getInstance(
+        java.util.Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE))
+      Some((i: Int) =>
+        walker.walk((s: java.util.stream.Stream[StackWalker.StackFrame]) =>
+          
s.skip(i.toLong).map[Class[?]](_.getDeclaringClass).findFirst().orElse(null)))
     } catch {
       case NonFatal(_) => None
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to