java-topology/defects/cpython/unit/CpythonTest.java

237 lines
8.7 KiB
Java

import java.util.*;
/**
* Java simulation of CPython CWE-407 defects.
*
* cpython-0001: codegen pattern-match stores duplicate check O(S^2)
* PySequence_Contains(pc->stores, name) inside loop → O(S^2)
* Fix: parallel HashSet for O(1) membership
*
* cpython-0002: typeobject pmerge() tail_contains O(M^2 * K)
* Linear scan of merge-list tails for each MRO candidate → O(M^2 * K)
* Fix: HashSet of tail elements for O(1) membership
*/
public class CpythonTest {
// ========== cpython-0001: pattern-match stores ==========
/** DEFECTIVE: O(S^2) — list.contains() per store name */
static long patternStoresDefective(int numStores) {
List<String> stores = new ArrayList<>();
long ops = 0;
for (int i = 0; i < numStores; i++) {
String name = "var_" + i;
// PySequence_Contains(pc->stores, name) — O(S)
boolean dup = stores.contains(name);
ops += stores.size();
if (!dup) {
stores.add(name);
}
}
return ops;
}
/** FIXED: O(S) — HashSet.contains() per store name */
static long patternStoresFixed(int numStores) {
List<String> stores = new ArrayList<>();
Set<String> storesSet = new HashSet<>();
long ops = 0;
for (int i = 0; i < numStores; i++) {
String name = "var_" + i;
// PySet_Contains(pc->stores_set, name) — O(1)
boolean dup = storesSet.contains(name);
ops++;
if (!dup) {
stores.add(name);
storesSet.add(name);
}
}
return ops;
}
// ========== cpython-0002: pmerge tail_contains ==========
/**
* Simulate C3 MRO linearization with linear tail_contains.
* Build diamond inheritance: C extends B1..BK, each Bi has MRO of length M/K.
*/
static long pmergeTailContainsDefective(int mroLen, int numBases) {
// Build merge lists: numBases lists, each of length mroLen/numBases
int listLen = Math.max(mroLen / numBases, 2);
List<List<String>> toLists = new ArrayList<>();
for (int k = 0; k < numBases; k++) {
List<String> lst = new ArrayList<>();
for (int j = 0; j < listLen; j++) {
lst.add("Class_" + k + "_" + j);
}
toLists.add(lst);
}
// Add the bases list itself
List<String> basesList = new ArrayList<>();
for (int k = 0; k < numBases; k++) {
basesList.add(toLists.get(k).get(0));
}
toLists.add(basesList);
int[] remain = new int[toLists.size()];
List<String> acc = new ArrayList<>();
long ops = 0;
boolean progress = true;
while (progress) {
progress = false;
for (int i = 0; i < toLists.size(); i++) {
List<String> cur = toLists.get(i);
if (remain[i] >= cur.size()) continue;
String candidate = cur.get(remain[i]);
boolean inTail = false;
// tail_contains: linear scan of tails — O(M)
for (int j = 0; j < toLists.size() && !inTail; j++) {
List<String> jLst = toLists.get(j);
for (int t = remain[j] + 1; t < jLst.size(); t++) {
ops++;
if (jLst.get(t).equals(candidate)) {
inTail = true;
break;
}
}
}
if (!inTail) {
acc.add(candidate);
for (int j = 0; j < toLists.size(); j++) {
List<String> jLst = toLists.get(j);
if (remain[j] < jLst.size() && jLst.get(remain[j]).equals(candidate)) {
remain[j]++;
}
}
progress = true;
break;
}
}
}
return ops;
}
/** FIXED: O(M * K) — HashSet for tail membership */
static long pmergeTailContainsFixed(int mroLen, int numBases) {
int listLen = Math.max(mroLen / numBases, 2);
List<List<String>> toLists = new ArrayList<>();
for (int k = 0; k < numBases; k++) {
List<String> lst = new ArrayList<>();
for (int j = 0; j < listLen; j++) {
lst.add("Class_" + k + "_" + j);
}
toLists.add(lst);
}
List<String> basesList = new ArrayList<>();
for (int k = 0; k < numBases; k++) {
basesList.add(toLists.get(k).get(0));
}
toLists.add(basesList);
int[] remain = new int[toLists.size()];
List<String> acc = new ArrayList<>();
long ops = 0;
// Build tail set: all elements in tail positions
Set<String> tailSet = new HashSet<>();
for (List<String> lst : toLists) {
for (int j = 1; j < lst.size(); j++) {
tailSet.add(lst.get(j));
}
}
boolean progress = true;
while (progress) {
progress = false;
for (int i = 0; i < toLists.size(); i++) {
List<String> cur = toLists.get(i);
if (remain[i] >= cur.size()) continue;
String candidate = cur.get(remain[i]);
ops++; // O(1) set lookup
boolean inTail = tailSet.contains(candidate);
if (!inTail) {
acc.add(candidate);
for (int j = 0; j < toLists.size(); j++) {
List<String> jLst = toLists.get(j);
if (remain[j] < jLst.size() && jLst.get(remain[j]).equals(candidate)) {
remain[j]++;
// Remove consumed head from tail set if new head exists
if (remain[j] < jLst.size()) {
// The element at remain[j] is now a head, not a tail
// But it may still be a tail in other lists, so don't remove
}
}
}
// Rebuild tail set (simplified; real fix would be incremental)
tailSet.clear();
for (List<String> lst : toLists) {
int r = remain[toLists.indexOf(lst)];
for (int t = r + 1; t < lst.size(); t++) {
tailSet.add(lst.get(t));
}
}
progress = true;
break;
}
}
}
return ops;
}
// ========== Test harness ==========
static void test0001() {
System.out.println("=== cpython-0001: codegen pattern-match stores ===");
int[] sizes = {10, 50, 100, 200};
for (int s : sizes) {
long defOps = patternStoresDefective(s);
long fixOps = patternStoresFixed(s);
double ratio = (double) defOps / fixOps;
System.out.printf(" S=%3d: defective=%6d fixed=%4d ratio=%.1fx%n",
s, defOps, fixOps, ratio);
if (s >= 50 && ratio < 5.0) {
throw new AssertionError("Expected ratio >= 5x at S=" + s
+ ", got " + ratio);
}
}
// Verify O(N^2) growth
long ops50 = patternStoresDefective(50);
long ops200 = patternStoresDefective(200);
double growth = (double) ops200 / ops50;
System.out.printf(" Growth 50->200: %.1fx (expect ~16x for O(N^2))%n", growth);
if (growth < 10.0) {
throw new AssertionError("Expected quadratic growth, got " + growth + "x");
}
System.out.println(" PASS");
}
static void test0002() {
System.out.println("=== cpython-0002: typeobject pmerge tail_contains ===");
int[][] params = {{20, 5}, {50, 10}, {100, 10}, {200, 10}};
for (int[] p : params) {
int M = p[0], K = p[1];
long defOps = pmergeTailContainsDefective(M, K);
long fixOps = pmergeTailContainsFixed(M, K);
double ratio = (double) defOps / Math.max(fixOps, 1);
System.out.printf(" M=%3d K=%2d: defective=%7d fixed=%5d ratio=%.1fx%n",
M, K, defOps, fixOps, ratio);
if (M >= 50 && ratio < 3.0) {
throw new AssertionError("Expected ratio >= 3x at M=" + M
+ ", got " + ratio);
}
}
System.out.println(" PASS");
}
public static void main(String[] args) {
test0001();
test0002();
System.out.println("\nAll CPython CWE-407 tests PASSED");
}
}