237 lines
8.7 KiB
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");
|
|
}
|
|
}
|