whitepaper: re-add 10 missing entries + 11 new defects this session, count 578→590; rebuild PDF
This commit is contained in:
parent
e4ee168b1e
commit
2e4f7807d5
401 changed files with 3914 additions and 114 deletions
|
|
@ -0,0 +1,99 @@
|
|||
# eclipse-jdt-0001: minimalErasedCandidates BFS work-queue ArrayList.contains O(N²)
|
||||
|
||||
**CWE-407** — Inefficient Algorithmic Complexity
|
||||
**Severity:** HIGH
|
||||
**Status:** PATCHED (patch below)
|
||||
|
||||
## Location
|
||||
|
||||
`org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java`
|
||||
Method: `minimalErasedCandidates(TypeBinding[], Map<TypeBinding,Object>)`
|
||||
Lines: **4273, 4295–4383** (current HEAD)
|
||||
|
||||
## Root Cause
|
||||
|
||||
`typesToVisit` is declared as `new ArrayList<>()` (line 4273).
|
||||
The BFS loop at line 4295 (`for (int i = 0; i < max; i++)`) expands the supertype
|
||||
hierarchy by appending to `typesToVisit` while walking it. For every candidate
|
||||
supertype it calls `typesToVisit.contains(superType)` to deduplicate — up to **5
|
||||
times per iteration** (elementType, Serializable, Cloneable, Object, each
|
||||
interface, superclass).
|
||||
|
||||
`ArrayList.contains` is O(N) linear scan. With N types in the common supertype
|
||||
hierarchy the BFS performs O(N) contains checks per step × O(N) steps =
|
||||
**O(N²) overall**.
|
||||
|
||||
This fires during every `lub()` / common-supertype computation, which is invoked:
|
||||
- for every conditional/ternary expression (`? :`)
|
||||
- for every multi-catch clause (`catch (A | B e)`)
|
||||
- for every intersection cast
|
||||
- for lambda return-type inference when multiple branches have different types
|
||||
|
||||
A class hierarchy with N=200 supertypes (achievable with deep interface diamond
|
||||
graphs or generated code) produces 40 000 list scans where 200 HashMap lookups
|
||||
would suffice.
|
||||
|
||||
## Defective Code
|
||||
|
||||
```java
|
||||
// Scope.java line 4273
|
||||
List<TypeBinding> typesToVisit = new ArrayList<>(); // <-- O(N) contains
|
||||
|
||||
// ... inside BFS at line 4295:
|
||||
for (int i = 0; i < max; i++) {
|
||||
...
|
||||
if (!typesToVisit.contains(superType)) { // O(N) × O(N) = O(N²)
|
||||
typesToVisit.add(superType);
|
||||
max++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
Replace the single `ArrayList` with a parallel `HashSet` used exclusively for
|
||||
deduplication; the `ArrayList` is kept for ordered iteration (the method later
|
||||
indexes into it).
|
||||
|
||||
```java
|
||||
// PATCHED
|
||||
List<TypeBinding> typesToVisit = new ArrayList<>();
|
||||
Set<TypeBinding> visitedSet = new HashSet<>(); // O(1) contains
|
||||
visitedSet.add(firstType);
|
||||
|
||||
// In the BFS loop, every !typesToVisit.contains(x) becomes:
|
||||
if (visitedSet.add(x)) { // Set.add returns false if already present
|
||||
typesToVisit.add(x);
|
||||
max++;
|
||||
}
|
||||
```
|
||||
|
||||
`Set.add` returns `false` when the element is already present, giving O(1) dedup
|
||||
while preserving the original ordered traversal semantics.
|
||||
|
||||
Note: `TypeBinding.equals` / `TypeBinding.hashCode` are already properly
|
||||
implemented in the JDT codebase (identity-based via `IdentityHashMap` usage
|
||||
elsewhere), so `HashSet<TypeBinding>` is safe here.
|
||||
|
||||
## Complexity Table
|
||||
|
||||
| Metric | Before (ArrayList) | After (HashSet dedup) |
|
||||
|--------|-------------------|----------------------|
|
||||
| Contains check | O(N) | O(1) |
|
||||
| BFS total work | O(N²) | O(N) |
|
||||
| Memory | O(N) | O(N) — one extra set |
|
||||
|
||||
## Speedup (measured in unit test, BRANCH=4)
|
||||
|
||||
| N (supertype count) | SLOW ops | FAST ops | Ratio |
|
||||
|---------------------|----------|----------|-------|
|
||||
| 50 | 4 870 | 184 | 26.5× |
|
||||
| 100 | 19 770 | 384 | 51.5× |
|
||||
| 200 | 79 570 | 784 | 101.5× |
|
||||
| 500 | 498 970 | 1 984 | 251.5× |
|
||||
|
||||
## Affected Callers
|
||||
|
||||
- `Scope.lub(TypeBinding[])` — line 4176: every `? :` ternary in compiled code
|
||||
- `Scope.lub(TypeBinding, TypeBinding)` — line 3749: pairwise common supertype
|
||||
- Any flow-analysis pass that calls `lub` (exception merging, return type merging)
|
||||
Loading…
Add table
Add a link
Reference in a new issue