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

paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 0a4de29ea9 GROOVY-12362: Ignore GraalVM native-image dispatch frames 
in caller lookup and sanitized traces
0a4de29ea9 is described below

commit 0a4de29ea95da22e39a4ccd004aa182e1d226342
Author: Paul King <[email protected]>
AuthorDate: Sun Sep 6 14:00:41 2026 +1000

    GROOVY-12362: Ignore GraalVM native-image dispatch frames in caller lookup 
and sanitized traces
    
    In a native image, dynamically built MethodHandle chains run in GraalVM's
    interpreter, whose com.oracle.svm.core.methodhandles frames are visible
    to StackWalker, and its reflection accessors live in
    com.oracle.svm.core.reflect. Treat com.oracle.svm.* as runtime frames in
    ReflectionUtils.getCallingClass (which affects getBundle and Grape) and
    in StackTraceUtils.sanitize. Use Class.getPackageName rather than
    getPackage, which is null for those classes inside an image.
---
 .../groovy/reflection/ReflectionUtils.java         | 16 +++++---
 .../codehaus/groovy/runtime/StackTraceUtils.java   |  3 +-
 src/test/groovy/bugs/Groovy12362.groovy            | 47 ++++++++++++++++++++++
 .../svm/core/methodhandles/FakeGraalFrame.groovy   | 35 ++++++++++++++++
 .../groovy/runtime/StackTraceUtilsTest.groovy      | 19 +++++++++
 5 files changed, 114 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java 
b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
index 5f7a94e912..2d24f54d73 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionUtils.java
@@ -322,11 +322,17 @@ public class ReflectionUtils {
     }
 
     private static boolean classShouldBeIgnored(final Class c, final 
Collection<String> extraIgnoredPackages) {
-        return (c != null
-                && (c.isSynthetic()
-                    || (c.getPackage() != null
-                        && (IGNORED_PACKAGES.contains(c.getPackage().getName())
-                          || 
extraIgnoredPackages.contains(c.getPackage().getName())))));
+        if (c == null) return false;
+        if (c.isSynthetic()) return true;
+        String packageName = c.getPackageName(); // never null, unlike 
getPackage()
+        if (packageName.isEmpty()) return false;
+        return IGNORED_PACKAGES.contains(packageName)
+                || extraIgnoredPackages.contains(packageName)
+                // GraalVM native image runs dynamically built MethodHandle 
chains in an
+                // interpreter whose frames 
(com.oracle.svm.core.methodhandles) are not
+                // hidden, and its reflection accessors live in 
com.oracle.svm.core.reflect;
+                // all are dispatch machinery, never the caller (GROOVY-12362)
+                || packageName.startsWith("com.oracle.svm.");
     }
 
     private static final MethodHandle IS_SEALED_METHODHANDLE;
diff --git a/src/main/java/org/codehaus/groovy/runtime/StackTraceUtils.java 
b/src/main/java/org/codehaus/groovy/runtime/StackTraceUtils.java
index 9e3a317874..1fad43c2da 100644
--- a/src/main/java/org/codehaus/groovy/runtime/StackTraceUtils.java
+++ b/src/main/java/org/codehaus/groovy/runtime/StackTraceUtils.java
@@ -72,7 +72,8 @@ public class StackTraceUtils {
                             "groovyjarjar," +
                             "com.sun.," +
                             "org.apache.groovy.," +
-                            "jdk.internal."
+                            "jdk.internal.," +
+                            "com.oracle.svm." // GraalVM native image: 
MethodHandle interpreter and reflection accessor frames (GROOVY-12362)
             ).split("[\\s,]+");
 
     private static final List<Closure> tests = new ArrayList<>();
diff --git a/src/test/groovy/bugs/Groovy12362.groovy 
b/src/test/groovy/bugs/Groovy12362.groovy
new file mode 100644
index 0000000000..cf22da06c5
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy12362.groovy
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 bugs
+
+import com.oracle.svm.core.methodhandles.FakeGraalFrame
+import org.junit.jupiter.api.Test
+
+import static org.junit.jupiter.api.Assertions.assertEquals
+
+/**
+ * Frames of GraalVM's native-image dispatch machinery ({@code 
com.oracle.svm.*})
+ * are runtime frames, not callers, and must be skipped by Groovy's caller
+ * lookup. {@code FakeGraalFrame} only borrows the package name. This test
+ * lives outside the MOP packages that the lookup ignores, so its own frames
+ * are observable.
+ */
+final class Groovy12362 {
+
+    @Test
+    void testGetCallingClassIgnoresGraalVMDispatchFrames() {
+        // chain: getCallingClass <- FakeGraalFrame (ignored) <- Relay <- this 
test;
+        // the immediate caller ignoring runtime frames is Relay's caller: 
this class
+        assertEquals(Groovy12362, Relay.callerSeenThroughGraalFrame())
+    }
+
+    static class Relay {
+        static Class callerSeenThroughGraalFrame() {
+            FakeGraalFrame.callerOf()
+        }
+    }
+}
diff --git 
a/src/test/groovy/com/oracle/svm/core/methodhandles/FakeGraalFrame.groovy 
b/src/test/groovy/com/oracle/svm/core/methodhandles/FakeGraalFrame.groovy
new file mode 100644
index 0000000000..dbe85c98f7
--- /dev/null
+++ b/src/test/groovy/com/oracle/svm/core/methodhandles/FakeGraalFrame.groovy
@@ -0,0 +1,35 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 com.oracle.svm.core.methodhandles
+
+import groovy.transform.CompileStatic
+import org.codehaus.groovy.reflection.ReflectionUtils
+
+/**
+ * Test fixture for GROOVY-12362: stands in for a frame of GraalVM's
+ * native-image MethodHandle interpreter, which lives in this package. Only
+ * the package name matters; the class has no relation to GraalVM.
+ */
+@CompileStatic
+final class FakeGraalFrame {
+
+    static Class callerOf() {
+        ReflectionUtils.getCallingClass()
+    }
+}
diff --git 
a/src/test/groovy/org/codehaus/groovy/runtime/StackTraceUtilsTest.groovy 
b/src/test/groovy/org/codehaus/groovy/runtime/StackTraceUtilsTest.groovy
index e0d114dfa2..3cb76fe8d7 100644
--- a/src/test/groovy/org/codehaus/groovy/runtime/StackTraceUtilsTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/runtime/StackTraceUtilsTest.groovy
@@ -54,6 +54,25 @@ class StackTraceUtilsTest {
         assertFalse(StackTraceUtils.isApplicationClass("com.sun.proxy.Proxy"))
         
assertFalse(StackTraceUtils.isApplicationClass("org.apache.groovy.util.Something"))
         
assertFalse(StackTraceUtils.isApplicationClass("jdk.internal.misc.Unsafe"))
+        // GROOVY-12362: GraalVM native image dispatch machinery
+        
assertFalse(StackTraceUtils.isApplicationClass("com.oracle.svm.core.methodhandles.Util_java_lang_invoke_MethodHandle"))
+        
assertFalse(StackTraceUtils.isApplicationClass("com.oracle.svm.core.reflect.SubstrateMethodAccessor"))
+    }
+
+    @Test
+    void testSanitizeStripsGraalVMDispatchFrames() {
+        // the frames a dynamic call leaves in a GraalVM native image 
(GROOVY-12362)
+        def t = new Exception('boom')
+        t.stackTrace = [
+                new StackTraceElement('Svc', 'work', 'Svc.groovy', 20),
+                new 
StackTraceElement('com.oracle.svm.core.methodhandles.Util_java_lang_invoke_MethodHandle',
 'invokeInternal', null, 259),
+                new 
StackTraceElement('java.lang.invoke.LambdaForm$NamedFunction', 
'invokeWithArguments', null, 107),
+                new 
StackTraceElement('com.oracle.svm.core.reflect.SubstrateMethodAccessor', 
'invoke', null, 118),
+                new 
StackTraceElement('org.codehaus.groovy.vmplugin.v8.IndyInterface', 
'aotDispatch', null, 604),
+                new StackTraceElement('App', 'main', 'App.groovy', 5),
+        ] as StackTraceElement[]
+        def sanitized = StackTraceUtils.sanitize(t)
+        assertEquals(['Svc.work', 'App.main'], sanitized.stackTrace.collect { 
"${it.className}.${it.methodName}".toString() })
     }
 
     @Test

Reply via email to