java-topology/defects/angelscript/unit/AngelScriptTest.java
russell@unturf.com 6bafc7f5e9 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
2026-03-27 11:57:12 -04:00

135 lines
5.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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");
}
}