/** * dubbo-0002: MethodWalker.walkHierarchy diamond recursion O(2^D) * * Demonstrates the defect: recursive hierarchy traversal without a visited set * causes O(2^D) work on diamond interface inheritance patterns. * * Compile: javac MethodWalkerDiamondAlgorithm.java * Run: java MethodWalkerDiamondAlgorithm */ import java.util.*; public class MethodWalkerDiamondAlgorithm { // Simulated class hierarchy node static class ClassNode { final String name; final ClassNode superClass; final List interfaces; final int methodCount; ClassNode(String name, ClassNode superClass, int methodCount, ClassNode... interfaces) { this.name = name; this.superClass = superClass; this.interfaces = Arrays.asList(interfaces); this.methodCount = methodCount; } } // ---- DEFECT: O(2^D) — no visited guard (mirrors MethodWalker.walkHierarchy) ---- static int[] defectCounter = {0}; // visit counter static Map> walkDefect(ClassNode clazz) { Map> methodsMap = new HashMap<>(); walkHierarchyDefect(clazz, methodsMap); return methodsMap; } static void walkHierarchyDefect(ClassNode clazz, Map> methodsMap) { defectCounter[0]++; // Add methods (no dedup for visited classes) for (int i = 0; i < clazz.methodCount; i++) { methodsMap.computeIfAbsent(clazz.name + ".method" + i, k -> new ArrayList<>()) .add(clazz.name); // duplicate if revisited } if (clazz.superClass != null) { walkHierarchyDefect(clazz.superClass, methodsMap); // no visited check } for (ClassNode itf : clazz.interfaces) { walkHierarchyDefect(itf, methodsMap); // no visited check — diamond = O(2^D) } } // ---- FIX: O(N) — visited HashSet guards recursion ---- static int[] fixCounter = {0}; static Map> walkFixed(ClassNode clazz) { Map> methodsMap = new HashMap<>(); Set visited = new HashSet<>(); walkHierarchyFixed(clazz, methodsMap, visited); return methodsMap; } static void walkHierarchyFixed(ClassNode clazz, Map> methodsMap, Set visited) { if (!visited.add(clazz)) { return; // already visited — skip } fixCounter[0]++; for (int i = 0; i < clazz.methodCount; i++) { methodsMap.computeIfAbsent(clazz.name + ".method" + i, k -> new ArrayList<>()) .add(clazz.name); } if (clazz.superClass != null) { walkHierarchyFixed(clazz.superClass, methodsMap, visited); } for (ClassNode itf : clazz.interfaces) { walkHierarchyFixed(itf, methodsMap, visited); } } // Build a diamond hierarchy of depth D // Base -> I1_L1, I2_L1 -> I1_L2, I2_L2 -> ... // at each level, 2 interfaces share all children from the level below static ClassNode buildDiamond(int depth) { if (depth == 0) { return new ClassNode("Base", null, 2); } ClassNode base = buildDiamond(depth - 1); ClassNode left = new ClassNode("L" + depth + "_A", null, 1, base); ClassNode right = new ClassNode("L" + depth + "_B", null, 1, base); return new ClassNode("Service_D" + depth, null, 3, left, right); } public static void main(String[] args) { System.out.println("=== dubbo-0002: MethodWalker.walkHierarchy diamond recursion ==="); System.out.println(); // Test basic diamond pattern (depth 1): // Base interface -> A, B -> ServiceImpl ClassNode base = new ClassNode("Base", null, 2); ClassNode interfaceA = new ClassNode("InterfaceA", null, 1, base); ClassNode interfaceB = new ClassNode("InterfaceB", null, 1, base); ClassNode service = new ClassNode("ServiceImpl", null, 3, interfaceA, interfaceB); System.out.println("--- Basic diamond (depth 1) ---"); System.out.println("Hierarchy: ServiceImpl -> {InterfaceA, InterfaceB} -> Base"); defectCounter[0] = 0; fixCounter[0] = 0; Map> defectResult = walkDefect(service); Map> fixResult = walkFixed(service); System.out.printf("Defect visits: %d%n", defectCounter[0]); System.out.printf("Fix visits: %d%n", fixCounter[0]); // Verify: defect adds Base methods twice (duplicate entries in list) List baseMethods0 = defectResult.get("Base.method0"); System.out.printf("Base.method0 entries (defect): %d (expect 2, showing duplicate work)%n", baseMethods0 != null ? baseMethods0.size() : 0); List baseMethodsFix = fixResult.get("Base.method0"); System.out.printf("Base.method0 entries (fix): %d (expect 1, no duplicates)%n", baseMethodsFix != null ? baseMethodsFix.size() : 0); boolean duplicatesRemoved = (baseMethodsFix == null || baseMethodsFix.size() == 1); System.out.printf("No duplicates in fix: %s%n%n", duplicatesRemoved ? "PASS" : "FAIL"); // Benchmark scaling: vary diamond depth System.out.println("--- Scaling: visits vs diamond depth ---"); System.out.printf("%-8s %15s %15s %10s%n", "Depth D", "Defect visits", "Fix visits", "Ratio"); System.out.printf("%-8s %15s %15s %10s%n", "-------", "-------------", "----------", "-----"); boolean allPass = true; for (int d = 1; d <= 8; d++) { ClassNode diamond = buildDiamond(d); defectCounter[0] = 0; fixCounter[0] = 0; walkDefect(diamond); walkFixed(diamond); int dv = defectCounter[0]; int fv = fixCounter[0]; double ratio = (double) dv / fv; System.out.printf("%-8d %15d %15d %9.1fx%n", d, dv, fv, ratio); // At depth >= 3, ratio should be >= 4x if (d >= 3 && ratio < 4.0) { System.out.printf(" FAIL: expected ratio >= 4x at depth %d%n", d); allPass = false; } } System.out.println(); // Verify fix result is correct (same method set as defect, just deduplicated) ClassNode diamond3 = buildDiamond(3); Map> defectResult3 = walkDefect(diamond3); Map> fixResult3 = walkFixed(diamond3); boolean sameKeys = defectResult3.keySet().equals(fixResult3.keySet()); System.out.printf("Same method keys (defect vs fix): %s%n", sameKeys ? "PASS" : "FAIL"); if (!sameKeys) allPass = false; if (!duplicatesRemoved) allPass = false; System.out.println(); System.out.printf("RESULT: %s%n", allPass ? "PASS" : "FAIL"); if (!allPass) System.exit(1); } }