spidermonkey-0005: GatherAvailableModuleAncestors O(M²) execList ContainsElement scan

This commit is contained in:
russell@unturf.com 2026-03-29 22:20:07 -04:00
parent 0fb709cb72
commit 421f3352c7
4 changed files with 494 additions and 0 deletions

View file

@ -0,0 +1,128 @@
package unit;
import java.util.*;
/**
* RSourceWalkClassGraphTest — CWE-407 test for r-source-0002
*
* Models .walkClassGraph() dedup logic:
* slow: match(names(exti), what) — O(S²) total across loop
* fast: hash-env membership — O(S) total
*
* Test: for S superclasses, slow does ~S*(S+1)/2 comparisons; fast does ~S.
* Ratio must be >= 5x at S=50.
*/
public class RSourceWalkClassGraphTest {
// --- SLOW: O(S²) ---
// match(names(exti), what): linear scan of what for each name in exti
static class SlowWalkClassGraph {
long comparisons = 0;
// Simulate .walkClassGraph accumulating superclasses
// Each iteration adds one new class with a chain of transitive supers
Set<String> walkGraph(List<String[]> classChains) {
// ext = accumulated known superclass names (as ordered list like R's named list)
List<String> what = new ArrayList<>();
for (String[] chain : classChains) {
// exti = chain of superclasses for this intermediate class
// Remove already-known: match(names(exti), what) — O(|chain| × |what|)
List<String> newOnes = new ArrayList<>();
for (String name : chain) {
boolean found = false;
for (String known : what) { // O(|what|) linear scan
comparisons++;
if (known.equals(name)) {
found = true;
break;
}
}
if (!found) newOnes.add(name);
}
what.addAll(newOnes);
}
return new LinkedHashSet<>(what);
}
}
// --- FAST: O(S) ---
// Hash-env membership: exists(name, envir=what_set)
static class FastWalkClassGraph {
long lookups = 0;
Set<String> walkGraph(List<String[]> classChains) {
Set<String> whatSet = new HashSet<>(); // the hash env
List<String> what = new ArrayList<>(); // ordered for reproducibility
for (String[] chain : classChains) {
// exti[!vapply(names(exti), exists, ...)] — O(|chain|)
List<String> newOnes = new ArrayList<>();
for (String name : chain) {
lookups++;
if (!whatSet.contains(name)) {
newOnes.add(name);
}
}
for (String name : newOnes) {
whatSet.add(name);
what.add(name);
}
}
return new LinkedHashSet<>(what);
}
}
// Build a diamond-heavy class hierarchy:
// S classes C_0..C_S-1, each inheriting from multiple earlier classes
static List<String[]> buildClassChains(int s) {
// C_i has transitive supers: C_0 through C_{i-1}
// walkClassGraph processes each intermediate
List<String[]> chains = new ArrayList<>();
for (int i = 0; i < s; i++) {
// The i-th class contributes i transitive superclasses
String[] chain = new String[i];
for (int j = 0; j < i; j++) {
chain[j] = "class_" + j;
}
chains.add(chain);
}
return chains;
}
public static void main(String[] args) {
int[] sizes = {10, 20, 50, 100};
System.out.println("RSourceWalkClassGraphTest — r-source-0002");
System.out.println(" Pattern: match(names(exti), what) O(S²) vs hash-env O(S)");
System.out.println();
int passed = 0;
int total = 0;
for (int s : sizes) {
List<String[]> chains = buildClassChains(s);
SlowWalkClassGraph slow = new SlowWalkClassGraph();
FastWalkClassGraph fast = new FastWalkClassGraph();
Set<String> slowResult = slow.walkGraph(chains);
Set<String> fastResult = fast.walkGraph(chains);
boolean sameResult = slowResult.equals(fastResult);
double ratio = slow.comparisons > 0 ? (double) slow.comparisons / Math.max(fast.lookups, 1) : 1.0;
boolean correctRatio = s >= 20 ? ratio >= 5.0 : ratio >= 2.0;
boolean pass = sameResult && correctRatio;
total++;
if (pass) passed++;
System.out.printf(" S=%-4d slow=%7d fast=%5d ratio=%5.1fx same=%b %s%n",
s, slow.comparisons, fast.lookups, ratio, sameResult,
pass ? "PASS" : "FAIL");
}
System.out.println();
System.out.printf("Result: %d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}