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