java-topology/defects/groovy/unit/GroovyVerifierDefaultParamsTest.java

117 lines
4.2 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.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* groovy-0002 — Verifier: Arrays.asList(params).contains(p) O(V×P) per generated variant
*
* Demonstrates CWE-407: Verifier.addDefaultParameterMethods and
* addDefaultParameterConstructors call Arrays.asList(params).contains(p) inside
* visitVariableExpression, allocating a new List and scanning linearly on every
* variable expression visit.
*
* Models the inner visitor call pattern:
* slow(): Arrays.asList(params).contains(p) — allocates list + O(P) per call
* fast(): pre-built HashSet.contains(p) — O(1) per call
*
* Ratio must be >= 5x at N=500.
*/
public class GroovyVerifierDefaultParamsTest {
/**
* Defective path: simulate the visitor visiting V variable expressions,
* each time calling Arrays.asList(params).contains(p).
*
* params: array of P "kept" parameters for this generated variant
* allParams: array of P+D parameters for the original method
* varExprs: V parameter references seen in the body
*
* Returns total element comparisons.
*/
static long slow(String[] params, String[] allParams, List<String> varExprs) {
long ops = 0;
for (String p : varExprs) {
// Arrays.asList(params).contains(p) — O(P) scan, new List allocation
boolean inParams = false;
for (String x : params) {
ops++;
if (x.equals(p)) { inParams = true; break; }
}
if (!inParams) {
// Arrays.asList(allParams).contains(p) — second O(P) scan
for (String x : allParams) {
ops++;
if (x.equals(p)) break;
}
}
}
return ops;
}
/**
* Fixed path: pre-build HashSet once before the visitor, then O(1) per call.
*
* Returns total element comparisons (each hash probe = 1 op).
*/
static long fast(Set<String> paramsSet, Set<String> allParamsSet, List<String> varExprs) {
long ops = 0;
for (String p : varExprs) {
ops++; // one hash probe
boolean inParams = paramsSet.contains(p);
if (!inParams) {
ops++; // second hash probe
allParamsSet.contains(p);
}
}
return ops;
}
public static void main(String[] args) {
int[] sizes = {100, 200, 500, 1000};
int pass = 0, total = 0;
boolean allPassed = true;
for (int N : sizes) {
// P = N parameters in the kept array (generated variant)
// V = N variable expressions in the body
String[] params = new String[N];
String[] allParams = new String[N + 10]; // original has 10 extra defaulted params
for (int i = 0; i < N; i++) {
params[i] = "param" + i;
allParams[i] = "param" + i;
}
for (int i = 0; i < 10; i++) {
allParams[N + i] = "default" + i;
}
// Variable expressions: mix of params that ARE in kept set and ones that aren't
// Force worst case: all var-exprs reference defaulted params (not in params[])
List<String> varExprs = new ArrayList<>();
for (int i = 0; i < N; i++) {
varExprs.add("default" + (i % 10));
}
Set<String> paramsSet = new HashSet<>(Arrays.asList(params));
Set<String> allParamsSet = new HashSet<>(Arrays.asList(allParams));
long slowOps = slow(params, allParams, varExprs);
long fastOps = fast(paramsSet, allParamsSet, varExprs);
double ratio = (double) slowOps / fastOps;
boolean pass1 = ratio >= 5.0;
total++;
if (pass1) pass++;
else allPassed = false;
System.out.printf("N=%4d slow=%8d fast=%6d ratio=%7.1fx %s%n",
N, slowOps, fastOps, ratio, pass1 ? "PASS" : "FAIL");
}
System.out.printf("%d/%d PASS%n", pass, total);
if (!allPassed) System.exit(1);
}
}