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

pjfanning 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 dfdd3eb55c Rework stack walking code to avoid Class.forName (#3354)
dfdd3eb55c is described below

commit dfdd3eb55cae3235911816803f1cef5b4bce56be
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Jul 15 20:49:50 2026 +0100

    Rework stack walking code to avoid Class.forName (#3354)
    
    * try to avoid Class.forName
    
    * scala3
    
    * Update TestKitUtils.scala
    
    * Update TestKitUtils.scala
    
    * Update MultiNodeClusterShardingConfig.scala
---
 .../sharding/MultiNodeClusterShardingConfig.scala  | 29 ++++++++++++++--------
 .../org/apache/pekko/testkit/TestKitUtils.scala    | 29 +++++++++++-----------
 2 files changed, 33 insertions(+), 25 deletions(-)

diff --git 
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
 
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
index 8db34c6839..532e8a6ddd 100644
--- 
a/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
+++ 
b/cluster-sharding/src/multi-jvm/scala/org/apache/pekko/cluster/sharding/MultiNodeClusterShardingConfig.scala
@@ -13,7 +13,9 @@
 
 package org.apache.pekko.cluster.sharding
 
+import java.lang.StackWalker.StackFrame
 import java.lang.reflect.Modifier
+import java.util.stream.{ Stream => JStream }
 
 import org.apache.pekko
 import pekko.cluster.MultiNodeClusterSpec
@@ -24,34 +26,39 @@ import com.typesafe.config.{ Config, ConfigFactory }
 
 object MultiNodeClusterShardingConfig {
 
+  private val stackWalker: java.util.function.Function[JStream[StackFrame], 
Array[Class[?]]] =
+    (frames: JStream[StackFrame]) =>
+      frames.map(_.getDeclaringClass).toArray[Class[?]]((size: Int) => new 
Array[Class[?]](size))
+
   private[sharding] def testNameFromCallStack(classToStartFrom: Class[?]): 
String = {
 
-    def isAbstractClass(className: String): Boolean = {
+    def isAbstractClass(clazz: Class[?]): Boolean = {
       try {
-        Modifier.isAbstract(Class.forName(className).getModifiers)
+        Modifier.isAbstract(clazz.getModifiers)
       } catch {
         case _: Throwable => false // yes catch everything, best effort check
       }
     }
 
     val startFrom = classToStartFrom.getName
-    val filteredStack = Thread.currentThread.getStackTrace.iterator
-      .map(_.getClassName)
+    val classes = 
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
+      .walk(stackWalker)
+    val filteredStack = classes.iterator
       // drop until we find the first occurrence of classToStartFrom
-      .dropWhile(!_.startsWith(startFrom))
+      .dropWhile(!_.getName.startsWith(startFrom))
       // then continue to the next entry after classToStartFrom that makes 
sense
       .dropWhile {
-        case `startFrom`                            => true
-        case str if str.startsWith(startFrom + "$") => true // lambdas inside 
startFrom etc
-        case str if isAbstractClass(str)            => true
-        case _                                      => false
+        case c if c.getName == startFrom                => true
+        case c if c.getName.startsWith(startFrom + "$") => true // lambdas 
inside startFrom etc
+        case c if isAbstractClass(c)                    => true
+        case _                                          => false
       }
 
     if (filteredStack.isEmpty)
-      throw new IllegalArgumentException(s"Couldn't find 
[${classToStartFrom.getName}] in call stack")
+      throw new IllegalArgumentException(s"Couldn't find [$startFrom] in call 
stack")
 
     // sanitize for actor system name
-    scrubActorSystemName(filteredStack.next())
+    scrubActorSystemName(filteredStack.next().getName)
   }
 
   /**
diff --git a/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala 
b/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
index b0def8b675..c726b90738 100644
--- a/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
+++ b/testkit/src/main/scala/org/apache/pekko/testkit/TestKitUtils.scala
@@ -27,39 +27,40 @@ import org.apache.pekko.annotation.InternalApi
 @InternalApi
 private[pekko] object TestKitUtils {
 
-  private val stackWalker: java.util.function.Function[JStream[StackFrame], 
Array[String]] =
+  private val stackWalker: java.util.function.Function[JStream[StackFrame], 
Array[Class[?]]] =
     (frames: JStream[StackFrame]) =>
-      frames.map(_.getClassName).toArray[String]((size: Int) => new 
Array[String](size))
+      frames.map(_.getDeclaringClass).toArray[Class[?]]((size: Int) => new 
Array[Class[?]](size))
 
   def testNameFromCallStack(classToStartFrom: Class[?], testKitRegex: Regex): 
String = {
 
-    def isAbstractClass(className: String): Boolean = {
+    def isAbstractClass(clazz: Class[?]): Boolean = {
       try {
-        Modifier.isAbstract(Class.forName(className).getModifiers)
+        Modifier.isAbstract(clazz.getModifiers)
       } catch {
         case _: Throwable => false // yes catch everything, best effort check
       }
     }
 
     val startFrom = classToStartFrom.getName
-    val classNames = StackWalker.getInstance().walk(stackWalker)
-    val filteredStack = classNames.iterator
+    val classes = 
StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
+      .walk(stackWalker)
+    val filteredStack = classes.iterator
       // drop until we find the first occurrence of classToStartFrom
-      .dropWhile(!_.startsWith(startFrom))
+      .dropWhile(!_.getName.startsWith(startFrom))
       // then continue to the next entry after classToStartFrom that makes 
sense
       .dropWhile {
-        case `startFrom`                            => true
-        case str if str.startsWith(startFrom + "$") => true // lambdas inside 
startFrom etc
-        case testKitRegex()                         => true // testkit 
internals
-        case str if isAbstractClass(str)            => true
-        case _                                      => false
+        case c if c.getName == startFrom                => true
+        case c if c.getName.startsWith(startFrom + "$") => true // lambdas 
inside startFrom etc
+        case c if testKitRegex.matches(c.getName)       => true // testkit 
internals
+        case c if isAbstractClass(c)                    => true
+        case _                                          => false
       }
 
     if (filteredStack.isEmpty)
-      throw new IllegalArgumentException(s"Couldn't find 
[${classToStartFrom.getName}] in call stack")
+      throw new IllegalArgumentException(s"Couldn't find [$startFrom] in call 
stack")
 
     // sanitize for actor system name
-    scrubActorSystemName(filteredStack.next())
+    scrubActorSystemName(filteredStack.next().getName)
   }
 
   /**


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

Reply via email to