130 lines
4.9 KiB
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);
|
|
}
|
|
}
|