124 lines
4.5 KiB
Java
124 lines
4.5 KiB
Java
package unit;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 unit test: Swift RequirementMachine isInMinimizationDomain
|
||
*
|
||
* Models the Swift compiler's RewriteSystem where:
|
||
* - Protos = list of protocol declarations in the minimization domain
|
||
* - Rules = rewrite rules over those protocols
|
||
*
|
||
* The defect: isInMinimizationDomain() does a linear scan over Protos
|
||
* on every call, and is called once per rule in loops over all rules.
|
||
* Total cost: O(R × P) where R = rule count, P = protocol count.
|
||
*
|
||
* The fix: replace the linear scan with a HashSet lookup — O(1) per call.
|
||
*
|
||
* Test measures ops count (comparisons) at N=800 (protos and rules both N).
|
||
*/
|
||
public class SwiftRequirementMachineAlgorithm {
|
||
|
||
// ---- Defective version: std::find equivalent (linear scan) ----------------
|
||
|
||
static long defectiveIsInDomain(List<Integer> protos, int proto) {
|
||
// models: std::find(Protos.begin(), Protos.end(), proto)
|
||
// returns ops performed
|
||
long ops = 0;
|
||
for (Integer p : protos) {
|
||
ops++;
|
||
if (p.equals(proto)) return ops;
|
||
}
|
||
return ops;
|
||
}
|
||
|
||
/**
|
||
* Simulate the rule-loop pattern from HomotopyReduction.cpp:611
|
||
* for (const auto &rule : getLocalRules()) {
|
||
* if (!isInMinimizationDomain(rule.getLHS().getRootProtocol())) continue;
|
||
* ...
|
||
* }
|
||
*/
|
||
static long defectiveMinimize(List<Integer> protos, List<Integer> rules) {
|
||
long totalOps = 0;
|
||
for (int rule : rules) {
|
||
// Each rule references a protocol; check membership linearly
|
||
totalOps += defectiveIsInDomain(protos, rule % protos.size());
|
||
}
|
||
return totalOps;
|
||
}
|
||
|
||
// ---- Fixed version: HashSet O(1) lookup -----------------------------------
|
||
|
||
static long fixedMinimize(Set<Integer> protoSet, List<Integer> rules) {
|
||
long totalOps = 0;
|
||
for (int rule : rules) {
|
||
// O(1) hash set lookup
|
||
totalOps++;
|
||
protoSet.contains(rule % protoSet.size());
|
||
}
|
||
return totalOps;
|
||
}
|
||
|
||
// ---- Test harness ----------------------------------------------------------
|
||
|
||
static final int N = 800;
|
||
|
||
public static void main(String[] args) {
|
||
int passed = 0;
|
||
int total = 0;
|
||
|
||
// Build protos list (P = N protocols) and rules list (R = N rules)
|
||
List<Integer> protos = new ArrayList<>(N);
|
||
for (int i = 0; i < N; i++) protos.add(i);
|
||
|
||
List<Integer> rules = new ArrayList<>(N);
|
||
for (int i = 0; i < N; i++) rules.add(i);
|
||
|
||
Set<Integer> protoSet = new HashSet<>(protos);
|
||
|
||
// Test 1: defective version is O(R*P) — ops >= N*N/2 on average
|
||
total++;
|
||
long slowOps = defectiveMinimize(protos, rules);
|
||
// Each rule on average scans half the protos list
|
||
long expectedMinSlow = (long) N * N / 4; // conservative lower bound
|
||
assert slowOps >= expectedMinSlow
|
||
: "Slow ops " + slowOps + " unexpectedly low, expected >= " + expectedMinSlow;
|
||
System.out.println("PASS test1: defective ops=" + slowOps + " (O(N^2) confirmed, N=" + N + ")");
|
||
passed++;
|
||
|
||
// Test 2: fixed version is O(R) — ops == N exactly
|
||
total++;
|
||
long fastOps = fixedMinimize(protoSet, rules);
|
||
assert fastOps == N
|
||
: "Fast ops should be exactly N=" + N + ", got " + fastOps;
|
||
System.out.println("PASS test2: fixed ops=" + fastOps + " (O(N) confirmed, N=" + N + ")");
|
||
passed++;
|
||
|
||
// Test 3: speedup is at least 10x
|
||
total++;
|
||
double speedup = (double) slowOps / fastOps;
|
||
assert speedup >= 10.0
|
||
: "Expected speedup >= 10x, got " + speedup;
|
||
System.out.printf("PASS test3: speedup=%.1fx%n", speedup);
|
||
passed++;
|
||
|
||
// Test 4: correctness — both find the same items in domain
|
||
total++;
|
||
List<Integer> smallProtos = Arrays.asList(10, 20, 30, 40, 50);
|
||
Set<Integer> smallSet = new HashSet<>(smallProtos);
|
||
|
||
// Probe 5 different values
|
||
int[] probeVals = {10, 25, 30, 99, 50};
|
||
for (int v : probeVals) {
|
||
boolean defectResult = defectiveIsInDomain(smallProtos, v) > 0 && smallProtos.contains(v);
|
||
boolean fixedResult = smallSet.contains(v);
|
||
assert defectResult == fixedResult
|
||
: "Mismatch for value " + v + ": defect=" + defectResult + " fixed=" + fixedResult;
|
||
}
|
||
System.out.println("PASS test4: correctness — defective and fixed agree on all probes");
|
||
passed++;
|
||
|
||
System.out.println(passed + "/" + total + " PASS");
|
||
}
|
||
}
|