java-topology/defects/firefox/test/Firefox0002DOMTokenListTest.java

130 lines
4.9 KiB
Java

/**
* CWE-407 simulation: Firefox nsDOMTokenList O(T²) classList add/remove.
*
* Simulates AddInternal() and RemoveInternal() from dom/base/nsDOMTokenList.cpp.
* The defective version uses ArrayList.contains() for dedup (O(T²)),
* the fixed version uses HashSet (O(T)).
*/
import java.util.*;
public class Firefox0002DOMTokenListTest {
// --- Defective AddInternal: addedClasses is ArrayList ---
static int addInternalDefective(List<String> existingClasses, List<String> tokens) {
List<String> addedClasses = new ArrayList<>();
int ops = 0;
for (String token : tokens) {
// Check if already in existing classes (linear)
ops++;
if (existingClasses.contains(token)) continue;
// Check if already added (linear in addedClasses)
ops++;
if (addedClasses.contains(token)) continue;
addedClasses.add(token);
}
return ops;
}
// --- Fixed AddInternal: addedClasses is HashSet ---
static int addInternalFixed(List<String> existingClasses, List<String> tokens) {
Set<String> existingSet = new HashSet<>(existingClasses);
Set<String> addedClasses = new HashSet<>();
int ops = 0;
for (String token : tokens) {
ops++;
if (existingSet.contains(token)) continue;
ops++;
if (addedClasses.contains(token)) continue;
addedClasses.add(token);
}
return ops;
}
// --- Defective RemoveInternal: aTokens.Contains() linear ---
static int removeInternalDefective(List<String> existingAtoms, List<String> tokens) {
int ops = 0;
for (String atom : existingAtoms) {
ops++;
if (tokens.contains(atom)) continue; // O(T) per atom
// would append to result
}
return ops;
}
// --- Fixed RemoveInternal: tokenSet is HashSet ---
static int removeInternalFixed(List<String> existingAtoms, List<String> tokens) {
Set<String> tokenSet = new HashSet<>(tokens);
int ops = 0;
for (String atom : existingAtoms) {
ops++;
if (tokenSet.contains(atom)) continue; // O(1)
}
return ops;
}
public static void main(String[] args) {
System.out.println("=== Firefox-0002: nsDOMTokenList O(T²) classList add/remove ===\n");
int[] tokenCounts = {50, 200, 500, 1000};
int pass = 0, fail = 0;
// Test AddInternal
System.out.println("--- AddInternal ---");
for (int T : tokenCounts) {
List<String> existing = new ArrayList<>();
for (int i = 0; i < 20; i++) existing.add("existing-" + i);
List<String> tokens = new ArrayList<>();
for (int i = 0; i < T; i++) tokens.add("new-token-" + i);
// Warmup
addInternalDefective(existing, tokens);
addInternalFixed(existing, tokens);
long t0 = System.nanoTime();
for (int r = 0; r < 200; r++) addInternalDefective(existing, tokens);
long defectiveNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 200; r++) addInternalFixed(existing, tokens);
long fixedNs = System.nanoTime() - t0;
double ratio = (double) defectiveNs / fixedNs;
boolean ok = ratio > 1.5;
System.out.printf("T=%4d | defective=%8.3fms fixed=%8.3fms ratio=%.1fx %s%n",
T, defectiveNs / 1e6, fixedNs / 1e6, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
// Test RemoveInternal
System.out.println("\n--- RemoveInternal ---");
for (int T : tokenCounts) {
List<String> atoms = new ArrayList<>();
for (int i = 0; i < T; i++) atoms.add("class-" + i);
List<String> tokensToRemove = new ArrayList<>();
for (int i = 0; i < T / 2; i++) tokensToRemove.add("class-" + (i * 2));
// Warmup
removeInternalDefective(atoms, tokensToRemove);
removeInternalFixed(atoms, tokensToRemove);
long t0 = System.nanoTime();
for (int r = 0; r < 200; r++) removeInternalDefective(atoms, tokensToRemove);
long defectiveNs = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < 200; r++) removeInternalFixed(atoms, tokensToRemove);
long fixedNs = System.nanoTime() - t0;
double ratio = (double) defectiveNs / fixedNs;
boolean ok = ratio > 1.5;
System.out.printf("T=%4d | defective=%8.3fms fixed=%8.3fms ratio=%.1fx %s%n",
T, defectiveNs / 1e6, fixedNs / 1e6, ratio, ok ? "PASS" : "FAIL");
if (ok) pass++; else fail++;
}
System.out.printf("%nResults: %d PASS, %d FAIL%n", pass, fail);
if (fail > 0) System.exit(1);
}
}