Skip to content

Commit bdfb672

Browse files
committed
GROOVY-11892: Enhance the groovy.extension.disable system property mechanism
1 parent c45c74e commit bdfb672

1 file changed

Lines changed: 115 additions & 3 deletions

File tree

src/main/java/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public class MetaClassRegistryImpl implements MetaClassRegistry {
8484
private final ExtensionModuleRegistry moduleRegistry = new ExtensionModuleRegistry();
8585
private final String disabledString = SystemUtil.getSystemPropertySafe(EXTENSION_DISABLE_PROPERTY);
8686
private final boolean disabling = disabledString != null;
87-
private final Set<String> disabledNames = disabling ? new HashSet<>(Arrays.asList(disabledString.split(","))) : null;
87+
private final List<DisabledMethodSpec> disabledSpecs = disabling ? DisabledMethodSpec.parse(disabledString) : null;
8888

8989
public static final int LOAD_DEFAULT = 0;
9090
public static final int DONT_LOAD_DEFAULT = 1;
@@ -207,7 +207,7 @@ private void registerMethods(final Class theClass, final boolean useMethodWrappe
207207
List<GeneratedMetaMethod.DgmMethodRecord> records = GeneratedMetaMethod.DgmMethodRecord.loadDgmInfo();
208208

209209
for (GeneratedMetaMethod.DgmMethodRecord record : records) {
210-
if (disabling && disabledNames.contains(record.methodName)) continue;
210+
if (disabling && isDisabled(record.methodName, record.parameters)) continue;
211211
Class[] newParams = new Class[record.parameters.length - 1];
212212
System.arraycopy(record.parameters, 1, newParams, 0, newParams.length);
213213

@@ -235,7 +235,7 @@ private void registerMethods(final Class theClass, final boolean useMethodWrappe
235235
CachedMethod[] methods = ReflectionCache.getCachedClass(theClass).getMethods();
236236
for (CachedMethod method : methods) {
237237
if (method.isStatic() && method.isPublic() && method.getAnnotation(Deprecated.class) == null) {
238-
if (disabling && disabledNames.contains(method.getName())) continue;
238+
if (disabling && isDisabled(method.getName(), method.getParameterTypes())) continue;
239239
CachedClass[] paramTypes = method.getParameterTypes();
240240
if (paramTypes.length > 0) {
241241
List<MetaMethod> arr = map.computeIfAbsent(paramTypes[0], k -> new ArrayList<MetaMethod>(4));
@@ -540,4 +540,116 @@ public void onModule(final ExtensionModule module) {
540540
}
541541
}
542542
}
543+
544+
// DGM records use Class[]
545+
private boolean isDisabled(String methodName, Class[] parameters) {
546+
for (DisabledMethodSpec spec : disabledSpecs) {
547+
if (spec.matches(methodName, parameters)) return true;
548+
}
549+
return false;
550+
}
551+
552+
// Extension module methods use CachedClass[]
553+
private boolean isDisabled(String methodName, CachedClass[] parameters) {
554+
for (DisabledMethodSpec spec : disabledSpecs) {
555+
if (spec.matches(methodName, parameters)) return true;
556+
}
557+
return false;
558+
}
559+
560+
/**
561+
* Represents a parsed entry from the {@code groovy.extension.disable} system property.
562+
* <p>
563+
* Supported forms:
564+
* <ul>
565+
* <li>{@code methodName} — matches all overloads</li>
566+
* <li>{@code methodName(type)} — matches by receiver type (first parameter)</li>
567+
* <li>{@code methodName(type1,type2,...)} — matches the exact parameter signature</li>
568+
* </ul>
569+
*/
570+
private static final class DisabledMethodSpec {
571+
private final String name;
572+
private final String[] paramTypes; // null means match all overloads
573+
574+
DisabledMethodSpec(String name, String[] paramTypes) {
575+
this.name = name;
576+
this.paramTypes = paramTypes;
577+
}
578+
579+
boolean matches(String methodName, Class[] parameters) {
580+
if (!name.equals(methodName)) return false;
581+
if (paramTypes == null) return true;
582+
if (paramTypes.length != parameters.length) return false;
583+
for (int i = 0; i < paramTypes.length; i++) {
584+
if (!matchesType(paramTypes[i], parameters[i])) return false;
585+
}
586+
return true;
587+
}
588+
589+
boolean matches(String methodName, CachedClass[] parameters) {
590+
if (!name.equals(methodName)) return false;
591+
if (paramTypes == null) return true;
592+
if (paramTypes.length != parameters.length) return false;
593+
for (int i = 0; i < paramTypes.length; i++) {
594+
if (!matchesType(paramTypes[i], parameters[i].getTheClass())) return false;
595+
}
596+
return true;
597+
}
598+
599+
// Allows simple name ("Set"), fully qualified name ("java.util.Set"),
600+
// and array notation ("Object[]", "boolean[][]", etc.).
601+
// Uses Class.getCanonicalName() which produces human-readable names
602+
// like "java.lang.Object[]" instead of JVM names like "[Ljava.lang.Object;".
603+
private static boolean matchesType(String spec, Class<?> type) {
604+
String canonical = type.getCanonicalName();
605+
if (spec.equals(canonical)) return true;
606+
// allow simple name to match: "Set" matches "java.util.Set",
607+
// "Object[]" matches "java.lang.Object[]"
608+
String simpleName = type.getSimpleName();
609+
return spec.equals(simpleName);
610+
}
611+
612+
/**
613+
* Parses the comma-separated disable property value, respecting parentheses.
614+
* For example: {@code "asChecked,toSorted(Set,Class),collect"} produces three specs.
615+
*/
616+
static List<DisabledMethodSpec> parse(String input) {
617+
List<DisabledMethodSpec> specs = new ArrayList<>();
618+
for (String entry : splitRespectingParens(input)) {
619+
entry = entry.trim();
620+
if (entry.isEmpty()) continue;
621+
int parenIdx = entry.indexOf('(');
622+
if (parenIdx < 0) {
623+
specs.add(new DisabledMethodSpec(entry, null));
624+
} else {
625+
String name = entry.substring(0, parenIdx);
626+
String paramStr = entry.substring(parenIdx + 1, entry.length() - 1);
627+
if (paramStr.isEmpty()) {
628+
specs.add(new DisabledMethodSpec(name, new String[0]));
629+
} else {
630+
String[] params = paramStr.split(",");
631+
for (int i = 0; i < params.length; i++) params[i] = params[i].trim();
632+
specs.add(new DisabledMethodSpec(name, params));
633+
}
634+
}
635+
}
636+
return specs;
637+
}
638+
639+
private static List<String> splitRespectingParens(String input) {
640+
List<String> result = new ArrayList<>();
641+
int depth = 0, start = 0;
642+
for (int i = 0; i < input.length(); i++) {
643+
char c = input.charAt(i);
644+
if (c == '(') depth++;
645+
else if (c == ')') depth--;
646+
else if (c == ',' && depth == 0) {
647+
result.add(input.substring(start, i));
648+
start = i + 1;
649+
}
650+
}
651+
result.add(input.substring(start));
652+
return result;
653+
}
654+
}
543655
}

0 commit comments

Comments
 (0)