sfml/angelscript/threejs/pygame: unit tests + patches + whitepaper (115 sites, 48 ecosystems)

SFML 5 defects: VideoMode dedup x3 (139x), allWindows erase (1001x), GL ext (149x)
AngelScript 3 defects: FindNewOwner (100x), CompileSwitch (250x)
Three.js 5 defects: WebGL binding (22x), StackNode filter (1875x), NodeBuilder (517x)
pygame 4 defects: remove_internal (3001x), spritecollide dokill (3001x), switch_layer (3001x)

Whitepaper: 98→115 sites, 44→48 ecosystems
All unit tests pass: 6/6 each
This commit is contained in:
russell@unturf.com 2026-03-27 11:57:12 -04:00
parent 442a6156e3
commit 6bafc7f5e9
17 changed files with 1189 additions and 18 deletions

View file

@ -0,0 +1,135 @@
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<List<Integer>> moduleTypes = new ArrayList<>();
for (int m = 0; m < modules; m++) {
List<Integer> 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<Integer> 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<Set<Integer>> moduleSets = new ArrayList<>();
for (int m = 0; m < modules; m++) {
Set<Integer> 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<Integer> 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<Integer> caseValues = new ArrayList<>();
Set<Integer> 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");
}
}