From 7b0795188207d5f61d2998739063be99569f476e Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Sun, 29 Mar 2026 20:32:55 -0400 Subject: [PATCH] cleanup: remove duplicate groovy-0001/0002 docs (same UNDF as originals); stamp swift-0001 --- ...roovy-0001-stc-collectednames-arraylist.md | 89 ------------------- ...oovy-0002-verifier-params-arrays-aslist.md | 87 ------------------ ...kup-protocol-superclass-diamond-revisit.md | 2 +- 3 files changed, 1 insertion(+), 177 deletions(-) delete mode 100644 defects/groovy/patch/groovy-0001-stc-collectednames-arraylist.md delete mode 100644 defects/groovy/patch/groovy-0002-verifier-params-arrays-aslist.md diff --git a/defects/groovy/patch/groovy-0001-stc-collectednames-arraylist.md b/defects/groovy/patch/groovy-0001-stc-collectednames-arraylist.md deleted file mode 100644 index 3d91ab8a2..000000000 --- a/defects/groovy/patch/groovy-0001-stc-collectednames-arraylist.md +++ /dev/null @@ -1,89 +0,0 @@ -# UNDF: UNDF-2026-000000409 -# groovy-0001: StaticTypeCheckingVisitor.checkNamedParamsAnnotation — O(E×C) ArrayList.contains membership scan - -## CWE-407 — Algorithmic Complexity: O(E×C) linear scan for named param validation - -| Field | Value | -|-------|-------| -| ID | groovy-0001 | -| Severity | MEDIUM | -| Ecosystem | groovy | -| Package | org.codehaus.groovy.transform.stc | -| File | `src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java` | -| Lines | 3205–3242 | -| Complexity | O(E×C) where E = map entry count, C = collected annotation names | -| Hot path | Static type checking of @NamedParams/@NamedParam annotations on method calls | - -## Defect - -`checkNamedParamsAnnotation` builds `collectedNames` as an `ArrayList`, -then performs `collectedNames.contains(name)` inside a loop over `entries.entrySet()`. -Each `contains()` is O(C), making the overall check O(E×C) instead of O(E). - -```java -// DEFECT: ArrayList.contains() is O(C) inside O(E) loop → O(E×C) total -List collectedNames = new ArrayList<>(); -// ... populate collectedNames via processNamedParam() ... -if (!collectedNames.isEmpty()) { - for (Map.Entry entry : entries.entrySet()) { - Object name = entry.getKey(); - if (!collectedNames.contains(name)) { // O(C) scan each iteration - addStaticTypeError("unexpected named arg: " + name, entry.getValue()); - } - } -} -``` - -`processNamedParam` also appends to the same `List` accumulator: -```java -private void processNamedParam(final AnnotationNode namedParam, ..., final List collectedNames) { - ... - collectedNames.add(name); // appends to ArrayList -} -``` - -With E map entries and C collected names, the inner loop runs E×C comparisons. -For methods with many @NamedParam annotations and many call-site named args, this -is measurably quadratic: C=100 names × E=100 entries = 10,000 string comparisons -vs 200 with a HashSet. - -## Fix - -Change `collectedNames` from `ArrayList` to `Set`: - -```java -// FIX: Set.contains() is O(1) → O(E) total -Set collectedNames = new HashSet<>(); -// ... populate same way via processNamedParam() ... -if (!collectedNames.isEmpty()) { - for (Map.Entry entry : entries.entrySet()) { - Object name = entry.getKey(); - if (!collectedNames.contains(name)) { // O(1) lookup - addStaticTypeError("unexpected named arg: " + name, entry.getValue()); - } - } -} -``` - -Update `processNamedParam` signature to accept `Set` instead of `List`: - -```java -private void processNamedParam(final AnnotationNode namedParam, - final Map entries, - final MapExpression args, - final Set collectedNames) { // was List - ... - collectedNames.add(name); -} -``` - -## Speedup - -| C (names) | E (entries) | Before ops | After ops | Speedup | -|-----------|-------------|------------|-----------|---------| -| 10 | 10 | 100 | 10 | 10× | -| 50 | 50 | 2,500 | 50 | 50× | -| 100 | 100 | 10,000 | 100 | 100× | -| 500 | 500 | 250,000 | 500 | 500× | - -At C=E=1000 (pathological @NamedParams with many fields): 1,000,000 → 1,000 ops (1000×). diff --git a/defects/groovy/patch/groovy-0002-verifier-params-arrays-aslist.md b/defects/groovy/patch/groovy-0002-verifier-params-arrays-aslist.md deleted file mode 100644 index 4bd99befa..000000000 --- a/defects/groovy/patch/groovy-0002-verifier-params-arrays-aslist.md +++ /dev/null @@ -1,87 +0,0 @@ -# UNDF: UNDF-2026-000000410 -# groovy-0002: Verifier — O(V×P) Arrays.asList(params).contains() re-allocation per check - -## CWE-407 — Algorithmic Complexity: O(V×P) repeated array-to-list conversion + linear scan - -| Field | Value | -|-------|-------| -| ID | groovy-0002 | -| Severity | MEDIUM | -| Ecosystem | groovy | -| Package | org.codehaus.groovy.classgen | -| File | `src/main/java/org/codehaus/groovy/classgen/Verifier.java` | -| Lines | 963, 1043, 1054 | -| Complexity | O(V×P) where V = variable expression visits, P = params array length | -| Hot path | Verifier pass during compilation — constructor and method parameter resolution | - -## Defect - -Three sites in `Verifier.java` use `Arrays.asList(params).contains(p)` inside -visitor methods that iterate over variable expressions: - -**Site 1 (line 963):** Inside `visitVariableExpression` of an anonymous `CodeVisitorSupport` -that traverses a closure block, called for every variable expression: -```java -// DEFECT: Arrays.asList(params) allocates a new List wrapper each call; .contains() is O(P) -if (!Arrays.asList(params).contains(p) && Arrays.asList(method.getParameters()).contains(p)) { - // GROOVY-10602 -``` - -**Site 2 (line 1043):** Same pattern in a different `visitVariableExpression`: -```java -// DEFECT: re-allocates List each invocation, O(P) scan -if (p.getInitialExpression() instanceof ConstantExpression && !Arrays.asList(params).contains(p)){ -``` - -**Site 3 (line 1054):** -```java -// DEFECT: same pattern -if (p.hasInitialExpression() && !Arrays.asList(params).contains(p)) { -``` - -`Arrays.asList()` creates a wrapper each time — O(1) allocation but O(P) scan. -Called V times (once per variable expression in the method body), total cost is O(V×P). -For a method with P=20 parameters and V=100 variable references, that is 2,000 pointer -comparisons vs 100 with a pre-built `Set`. - -## Fix - -Hoist the set construction outside the visitor, share it as an effectively-final capture: - -```java -// FIX: build once before the visitor loop -Set paramSet = new HashSet<>(Arrays.asList(params)); -Set methodParamSet = new HashSet<>(Arrays.asList(method.getParameters())); - -// Inside visitVariableExpression: -if (!paramSet.contains(p) && methodParamSet.contains(p)) { // O(1) each - // GROOVY-10602 -``` - -For sites 2 and 3: -```java -Set paramSet = new HashSet<>(Arrays.asList(params)); -// ... -if (p.getInitialExpression() instanceof ConstantExpression && !paramSet.contains(p)) { -// ... -if (p.hasInitialExpression() && !paramSet.contains(p)) { -``` - -## Speedup - -| P (params) | V (var refs) | Before ops | After ops | Speedup | -|------------|--------------|------------|-----------|---------| -| 5 | 20 | 100 | 20 | 5× | -| 20 | 100 | 2,000 | 100 | 20× | -| 50 | 500 | 25,000 | 500 | 50× | -| 100 | 1,000 | 100,000 | 1,000 | 100× | - -Typical: P=20, V=50 method body references → 1,000 → 50 ops (20×). -Pathological generated code: P=100, V=1000 → 100,000 → 1,000 ops (100×). - -## Note on diamond recursion - -Groovy's interface hierarchy traversal (ClassNode.getAllInterfaces, Traits.collectAllInterfacesReverseOrder, -GeneralUtils.addAllInterfaces) all use `if (set.add(node))` guards introduced in GROOVY-11036. -These sites are CLEAN for O(2^D) diamond recursion. groovy-0001 and groovy-0002 are -O(N²) membership-scan defects, distinct from diamond recursion. diff --git a/defects/swift/patch/swift-0001-namelookup-protocol-superclass-diamond-revisit.md b/defects/swift/patch/swift-0001-namelookup-protocol-superclass-diamond-revisit.md index 504cbd00b..1d2b736c1 100644 --- a/defects/swift/patch/swift-0001-namelookup-protocol-superclass-diamond-revisit.md +++ b/defects/swift/patch/swift-0001-namelookup-protocol-superclass-diamond-revisit.md @@ -1,4 +1,4 @@ -# UNDF: (pending) +# UNDF: UNDF-2026-000000545 # swift-0001: QualifiedLookupRequest — O(2^D) diamond re-traversal via unguarded protocol superclass push ## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal