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

paulk 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 bdfb672792 GROOVY-11892: Enhance the groovy.extension.disable system 
property mechanism
bdfb672792 is described below

commit bdfb672792c90d447a0b770b6c80724b28079e0f
Author: Paul King <[email protected]>
AuthorDate: Tue Mar 31 22:53:44 2026 +1000

    GROOVY-11892: Enhance the groovy.extension.disable system property mechanism
---
 .../runtime/metaclass/MetaClassRegistryImpl.java   | 118 ++++++++++++++++++++-
 1 file changed, 115 insertions(+), 3 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
 
b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
index 2c60bb4762..08981c4d4c 100644
--- 
a/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
+++ 
b/src/main/java/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
@@ -84,7 +84,7 @@ public class MetaClassRegistryImpl implements 
MetaClassRegistry {
     private final ExtensionModuleRegistry moduleRegistry = new 
ExtensionModuleRegistry();
     private final String disabledString = 
SystemUtil.getSystemPropertySafe(EXTENSION_DISABLE_PROPERTY);
     private final boolean disabling = disabledString != null;
-    private final Set<String> disabledNames = disabling ? new 
HashSet<>(Arrays.asList(disabledString.split(","))) : null;
+    private final List<DisabledMethodSpec> disabledSpecs = disabling ? 
DisabledMethodSpec.parse(disabledString) : null;
 
     public static final int LOAD_DEFAULT = 0;
     public static final int DONT_LOAD_DEFAULT = 1;
@@ -207,7 +207,7 @@ public class MetaClassRegistryImpl implements 
MetaClassRegistry {
                 List<GeneratedMetaMethod.DgmMethodRecord> records = 
GeneratedMetaMethod.DgmMethodRecord.loadDgmInfo();
 
                 for (GeneratedMetaMethod.DgmMethodRecord record : records) {
-                    if (disabling && 
disabledNames.contains(record.methodName)) continue;
+                    if (disabling && isDisabled(record.methodName, 
record.parameters)) continue;
                     Class[] newParams = new Class[record.parameters.length - 
1];
                     System.arraycopy(record.parameters, 1, newParams, 0, 
newParams.length);
 
@@ -235,7 +235,7 @@ public class MetaClassRegistryImpl implements 
MetaClassRegistry {
             CachedMethod[] methods = 
ReflectionCache.getCachedClass(theClass).getMethods();
             for (CachedMethod method : methods) {
                 if (method.isStatic() && method.isPublic() && 
method.getAnnotation(Deprecated.class) == null) {
-                    if (disabling && disabledNames.contains(method.getName())) 
continue;
+                    if (disabling && isDisabled(method.getName(), 
method.getParameterTypes())) continue;
                     CachedClass[] paramTypes = method.getParameterTypes();
                     if (paramTypes.length > 0) {
                         List<MetaMethod> arr = 
map.computeIfAbsent(paramTypes[0], k -> new ArrayList<MetaMethod>(4));
@@ -540,4 +540,116 @@ public class MetaClassRegistryImpl implements 
MetaClassRegistry {
             }
         }
     }
+
+    // DGM records use Class[]
+    private boolean isDisabled(String methodName, Class[] parameters) {
+        for (DisabledMethodSpec spec : disabledSpecs) {
+            if (spec.matches(methodName, parameters)) return true;
+        }
+        return false;
+    }
+
+    // Extension module methods use CachedClass[]
+    private boolean isDisabled(String methodName, CachedClass[] parameters) {
+        for (DisabledMethodSpec spec : disabledSpecs) {
+            if (spec.matches(methodName, parameters)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Represents a parsed entry from the {@code groovy.extension.disable} 
system property.
+     * <p>
+     * Supported forms:
+     * <ul>
+     *   <li>{@code methodName} — matches all overloads</li>
+     *   <li>{@code methodName(type)} — matches by receiver type (first 
parameter)</li>
+     *   <li>{@code methodName(type1,type2,...)} — matches the exact parameter 
signature</li>
+     * </ul>
+     */
+    private static final class DisabledMethodSpec {
+        private final String name;
+        private final String[] paramTypes; // null means match all overloads
+
+        DisabledMethodSpec(String name, String[] paramTypes) {
+            this.name = name;
+            this.paramTypes = paramTypes;
+        }
+
+        boolean matches(String methodName, Class[] parameters) {
+            if (!name.equals(methodName)) return false;
+            if (paramTypes == null) return true;
+            if (paramTypes.length != parameters.length) return false;
+            for (int i = 0; i < paramTypes.length; i++) {
+                if (!matchesType(paramTypes[i], parameters[i])) return false;
+            }
+            return true;
+        }
+
+        boolean matches(String methodName, CachedClass[] parameters) {
+            if (!name.equals(methodName)) return false;
+            if (paramTypes == null) return true;
+            if (paramTypes.length != parameters.length) return false;
+            for (int i = 0; i < paramTypes.length; i++) {
+                if (!matchesType(paramTypes[i], parameters[i].getTheClass())) 
return false;
+            }
+            return true;
+        }
+
+        // Allows simple name ("Set"), fully qualified name ("java.util.Set"),
+        // and array notation ("Object[]", "boolean[][]", etc.).
+        // Uses Class.getCanonicalName() which produces human-readable names
+        // like "java.lang.Object[]" instead of JVM names like 
"[Ljava.lang.Object;".
+        private static boolean matchesType(String spec, Class<?> type) {
+            String canonical = type.getCanonicalName();
+            if (spec.equals(canonical)) return true;
+            // allow simple name to match: "Set" matches "java.util.Set",
+            // "Object[]" matches "java.lang.Object[]"
+            String simpleName = type.getSimpleName();
+            return spec.equals(simpleName);
+        }
+
+        /**
+         * Parses the comma-separated disable property value, respecting 
parentheses.
+         * For example: {@code "asChecked,toSorted(Set,Class),collect"} 
produces three specs.
+         */
+        static List<DisabledMethodSpec> parse(String input) {
+            List<DisabledMethodSpec> specs = new ArrayList<>();
+            for (String entry : splitRespectingParens(input)) {
+                entry = entry.trim();
+                if (entry.isEmpty()) continue;
+                int parenIdx = entry.indexOf('(');
+                if (parenIdx < 0) {
+                    specs.add(new DisabledMethodSpec(entry, null));
+                } else {
+                    String name = entry.substring(0, parenIdx);
+                    String paramStr = entry.substring(parenIdx + 1, 
entry.length() - 1);
+                    if (paramStr.isEmpty()) {
+                        specs.add(new DisabledMethodSpec(name, new String[0]));
+                    } else {
+                        String[] params = paramStr.split(",");
+                        for (int i = 0; i < params.length; i++) params[i] = 
params[i].trim();
+                        specs.add(new DisabledMethodSpec(name, params));
+                    }
+                }
+            }
+            return specs;
+        }
+
+        private static List<String> splitRespectingParens(String input) {
+            List<String> result = new ArrayList<>();
+            int depth = 0, start = 0;
+            for (int i = 0; i < input.length(); i++) {
+                char c = input.charAt(i);
+                if (c == '(') depth++;
+                else if (c == ')') depth--;
+                else if (c == ',' && depth == 0) {
+                    result.add(input.substring(start, i));
+                    start = i + 1;
+                }
+            }
+            result.add(input.substring(start));
+            return result;
+        }
+    }
 }

Reply via email to