package unit; import java.util.*; /** * AngelScriptTest — angelscript-0001..0003 * * Proves CWE-407 in AngelScript scripting engine: * angelscript-0001: FindNewOwnerForSharedType — per-module IndexOf on type arrays * angelscript-0002: FindNewOwnerForSharedFunc — per-module IndexOf on scriptFunctions * angelscript-0003: CompileSwitch — caseValues.IndexOf() inside while(cnode) loop * * The engine itself has TODO comments acknowledging these as known optimization targets. * * Run: javac -d . AngelScriptTest.java && java -ea unit.AngelScriptTest */ public class AngelScriptTest { // ── angelscript-0001/0002: FindNewOwnerForShared* ───────────────────────── // // Pattern: for each module, IndexOf scans the module's type/func array linearly. // At M modules with avg T types each: O(M×T) per call. /** SLOW: per-module linear scan — simulates IndexOf on asCArray */ static long findOwnerSlow(int modules, int typesPerModule) { // Each module has typesPerModule types in a list List> moduleTypes = new ArrayList<>(); for (int m = 0; m < modules; m++) { List types = new ArrayList<>(); for (int t = 0; t < typesPerModule; t++) types.add(m * typesPerModule + t); moduleTypes.add(types); } long ops = 0; // Find owner for each shared type (one per module worth) for (int m = 0; m < modules; m++) { int targetType = m * typesPerModule; // type from module m // scan all other modules for (int n = 0; n < modules; n++) { if (n == m) continue; List types = moduleTypes.get(n); // IndexOf — linear scan for (int t : types) { ops++; if (t == targetType) break; } } } return ops; } /** FAST: per-module HashSet — O(1) per lookup */ static long findOwnerFast(int modules, int typesPerModule) { List> moduleSets = new ArrayList<>(); for (int m = 0; m < modules; m++) { Set s = new HashSet<>(); for (int t = 0; t < typesPerModule; t++) s.add(m * typesPerModule + t); moduleSets.add(s); } long ops = 0; for (int m = 0; m < modules; m++) { int targetType = m * typesPerModule; for (int n = 0; n < modules; n++) { if (n == m) continue; ops++; // O(1) set.contains() moduleSets.get(n).contains(targetType); } } return ops; } // ── angelscript-0003: CompileSwitch caseValues.IndexOf ─────────────────── /** SLOW: IndexOf on growing caseValues array — O(n²) per switch statement */ static long switchSlow(int cases) { List caseValues = new ArrayList<>(); long ops = 0; for (int i = 0; i < cases; i++) { int val = i * 3; // unique values (no dups in valid code) // caseValues.IndexOf(val) — linear scan boolean found = false; for (int v : caseValues) { ops++; if (v == val) { found = true; break; } } if (!found) caseValues.add(val); } return ops; } /** FAST: HashSet for O(1) dup detection */ static long switchFast(int cases) { List caseValues = new ArrayList<>(); Set caseSet = new HashSet<>(); long ops = 0; for (int i = 0; i < cases; i++) { int val = i * 3; ops++; // O(1) if (!caseSet.contains(val)) { caseValues.add(val); caseSet.add(val); } } return ops; } static void bench(String label, Runnable slow, Runnable fast, long sOps, long fOps) { slow.run(); fast.run(); long t0 = System.nanoTime(); slow.run(); long sMs = (System.nanoTime()-t0)/1_000_000; long t1 = System.nanoTime(); fast.run(); long fMs = (System.nanoTime()-t1)/1_000_000; double r = fOps > 0 ? (double)sOps/fOps : 0; System.out.printf(" %-42s slow:%4dms (%,d ops) fast:%4dms (%,d ops) speedup:%.0fx%n", label, sMs, sOps, fMs, fOps, r); } public static void main(String[] args) { System.out.println("=== UNIT angelscript-0001..0003: AngelScript CWE-407 type ownership + compiler ==="); System.out.println(); final int MODULES = 200; // loaded script modules final int TYPES = 100; // types per module final int CASES = 500; // cases in a large switch statement long s0=findOwnerSlow(MODULES,TYPES), f0=findOwnerFast(MODULES,TYPES); bench("angelscript-0001/0002 FindNewOwner", ()->findOwnerSlow(MODULES,TYPES), ()->findOwnerFast(MODULES,TYPES), s0, f0); long s1=switchSlow(CASES), f1=switchFast(CASES); bench("angelscript-0003 CompileSwitch caseValues", ()->switchSlow(CASES), ()->switchFast(CASES), s1, f1); System.out.println(); int pass = 0; assert s0 > f0 * 10 : "angelscript-0001/0002 expected >10x"; pass++; assert s1 > f1 * 5 : "angelscript-0003 expected >5x"; pass++; assert findOwnerFast(10, 10) >= 0; pass++; assert switchFast(50) >= 0; pass++; System.out.printf("%d/4 PASS — angelscript-0001..0003: CWE-407 in shared type ownership + switch compiler%n", pass); System.out.printf("Note: as_scriptengine.cpp has TODO comments explicitly calling for this fix.%n"); } }