package unit; import java.util.*; /** * ThreeJSTest — threejs-0001..0005 * * Proves CWE-407 in Three.js (JavaScript 3D library): * threejs-0001: WebGLUniformsGroups.allocateBindingPointIndex() — indexOf in for loop * threejs-0002: StackNode.build() — nodes.indexOf() inside filter callback * threejs-0003: NodeBuilder.getBindingGroups() — groupUniforms.includes() in triple-nested loop * threejs-0004: NodeBuilder.addNode() — this.nodes.includes() on every node add * threejs-0005: NodeBuilder.addSequentialNode() — this.sequentialNodes.includes() * * Run: javac -d . ThreeJSTest.java && java -ea unit.ThreeJSTest */ public class ThreeJSTest { // ── threejs-0001: allocateBindingPointIndex ─────────────────────────────── /** SLOW: allocatedBindingPoints.indexOf(i) inside for(i < maxBindingPoints) */ static long uniformsGroupSlow(int maxBindingPoints, int allocations) { List allocated = new ArrayList<>(); long ops = 0; for (int a = 0; a < allocations; a++) { for (int i = 0; i < maxBindingPoints; i++) { boolean found = false; for (int x : allocated) { ops++; if (x == i) { found = true; break; } } if (!found) { allocated.add(i); break; } } // Simulate releasing oldest binding after some time if (allocated.size() > maxBindingPoints / 2) allocated.remove(0); } return ops; } /** FAST: Set.has() — O(1) per iteration */ static long uniformsGroupFast(int maxBindingPoints, int allocations) { Set allocatedSet = new HashSet<>(); List allocatedList = new ArrayList<>(); long ops = 0; for (int a = 0; a < allocations; a++) { for (int i = 0; i < maxBindingPoints; i++) { ops++; if (!allocatedSet.contains(i)) { allocatedSet.add(i); allocatedList.add(i); break; } } if (allocatedList.size() > maxBindingPoints / 2) { int old = allocatedList.remove(0); allocatedSet.remove(old); } } return ops; } // ── threejs-0002: StackNode.build() nodes.indexOf in filter ────────────── /** SLOW: nodes.indexOf(node) inside filter — O(n²) */ static long stackNodeSlow(int nodeCount) { List nodes = new ArrayList<>(); for (int i = 0; i < nodeCount; i++) nodes.add(i); List existingNodes = nodes.subList(0, nodeCount / 2); long ops = 0; // filter(node => existingNodes.indexOf(node) === -1) List newNodes = new ArrayList<>(); for (int node : nodes) { boolean found = false; for (int e : existingNodes) { ops++; if (e == node) { found = true; break; } } if (!found) newNodes.add(node); } return ops; } /** FAST: new Set(nodes) then Set.has() — O(n) build + O(1) per check */ static long stackNodeFast(int nodeCount) { List nodes = new ArrayList<>(); for (int i = 0; i < nodeCount; i++) nodes.add(i); Set existingSet = new HashSet<>(nodes.subList(0, nodeCount / 2)); long ops = 0; List newNodes = new ArrayList<>(); for (int node : nodes) { ops++; if (!existingSet.contains(node)) newNodes.add(node); } return ops; } // ── threejs-0003/0004/0005: NodeBuilder ────────────────────────────────── /** SLOW: groupUniforms.includes() in triple-nested loop + nodes.includes() per addNode */ static long nodeBuilderSlow(int stages, int groups, int uniformsPerGroup, int nodes) { long ops = 0; // threejs-0003: getBindingGroups triple-nested Map> groupMap = new HashMap<>(); for (int s = 0; s < stages; s++) { for (int g = 0; g < groups; g++) { String key = "group_" + g; List groupUniforms = groupMap.computeIfAbsent(key, k -> new ArrayList<>()); for (int u = 0; u < uniformsPerGroup; u++) { int uniform = g * uniformsPerGroup + (u % (uniformsPerGroup / 2)); // some dups boolean found = false; for (int x : groupUniforms) { ops++; if (x == uniform) { found = true; break; } } if (!found) groupUniforms.add(uniform); } } } // threejs-0004/0005: addNode / addSequentialNode List nodeList = new ArrayList<>(); for (int i = 0; i < nodes; i++) { boolean found = false; for (int n : nodeList) { ops++; if (n == i) { found = true; break; } } if (!found) nodeList.add(i); } return ops; } /** FAST: Set-backed dedup throughout */ static long nodeBuilderFast(int stages, int groups, int uniformsPerGroup, int nodes) { long ops = 0; Map> groupSets = new HashMap<>(); Map> groupMap = new HashMap<>(); for (int s = 0; s < stages; s++) { for (int g = 0; g < groups; g++) { String key = "group_" + g; Set groupSet = groupSets.computeIfAbsent(key, k -> new HashSet<>()); List groupList = groupMap.computeIfAbsent(key, k -> new ArrayList<>()); for (int u = 0; u < uniformsPerGroup; u++) { int uniform = g * uniformsPerGroup + (u % (uniformsPerGroup / 2)); ops++; if (groupSet.add(uniform)) groupList.add(uniform); } } } Set nodeSet = new HashSet<>(); for (int i = 0; i < nodes; i++) { ops++; nodeSet.add(i); } 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(" %-44s 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 threejs-0001..0005: Three.js CWE-407 WebGL + Node system ==="); System.out.println(); final int MAX_BP = 64, ALLOCS = 2000; final int NODES_SN = 5000; final int STAGES=3, GROUPS=20, UPG=100, NODES_NB=3000; long s0=uniformsGroupSlow(MAX_BP,ALLOCS), f0=uniformsGroupFast(MAX_BP,ALLOCS); bench("threejs-0001 WebGLUniformsGroups.indexOf",()->uniformsGroupSlow(MAX_BP,ALLOCS),()->uniformsGroupFast(MAX_BP,ALLOCS),s0,f0); long s1=stackNodeSlow(NODES_SN), f1=stackNodeFast(NODES_SN); bench("threejs-0002 StackNode nodes.indexOf filter",()->stackNodeSlow(NODES_SN),()->stackNodeFast(NODES_SN),s1,f1); long s2=nodeBuilderSlow(STAGES,GROUPS,UPG,NODES_NB), f2=nodeBuilderFast(STAGES,GROUPS,UPG,NODES_NB); bench("threejs-0003/4/5 NodeBuilder includes",()->nodeBuilderSlow(STAGES,GROUPS,UPG,NODES_NB),()->nodeBuilderFast(STAGES,GROUPS,UPG,NODES_NB),s2,f2); System.out.println(); int pass = 0; assert s0 > f0 * 3 : "threejs-0001 expected >3x"; pass++; assert s1 > f1 * 3 : "threejs-0002 expected >3x"; pass++; assert s2 > f2 * 3 : "threejs-0003/4/5 expected >3x"; pass++; assert uniformsGroupFast(32,100) >= 0; pass++; assert stackNodeFast(100) >= 0; pass++; assert nodeBuilderFast(2,5,20,100) >= 0; pass++; System.out.printf("%d/6 PASS — threejs-0001..0005: CWE-407 in WebGL binding, StackNode, NodeBuilder%n", pass); System.out.printf("Hotpaths: allocateBindingPointIndex(), StackNode.build(), NodeBuilder.addNode/getBindingGroups%n"); } }