88import org .junit .jupiter .api .Test ;
99
1010/**
11- * Verifies that no method in the generated parser or token manager
12- * exceeds the JVM's 64KB bytecode limit per method.
11+ * Verifies that no method in the generated parser or token manager exceeds the JVM's 64KB bytecode
12+ * limit per method.
1313 *
14- * <p>Catches the exact failure scenario:</p>
14+ * <p>
15+ * Catches the exact failure scenario:
16+ * </p>
17+ *
1518 * <pre>
1619 * org.objectweb.asm.MethodTooLargeException:
1720 * Method too large: net/sf/jsqlparser/parser/CCJSqlParserTokenManager.<clinit> ()V
1821 * </pre>
1922 *
20- * <p>This error occurs when bytecode instrumentation tools (JaCoCo, ASM, ByteBuddy)
21- * add probes to methods that are already near the 64KB limit. The tests therefore
22- * enforce a <b>safety margin</b> ({@value #INSTRUMENTATION_HEADROOM_PERCENT}%)
23- * below the hard JVM limit.</p>
23+ * <p>
24+ * This error occurs when bytecode instrumentation tools (JaCoCo, ASM, ByteBuddy) add probes to
25+ * methods that are already near the 64KB limit. The tests therefore enforce a <b>safety margin</b>
26+ * ({@value #INSTRUMENTATION_HEADROOM_PERCENT}%) below the hard JVM limit.
27+ * </p>
2428 *
25- * <p>Requires {@link BytecodeSizeVerifier} on the test classpath (same package).</p>
29+ * <p>
30+ * Requires {@link BytecodeSizeVerifier} on the test classpath (same package).
31+ * </p>
2632 */
2733public class BytecodeSizeTest {
2834
2935 /**
30- * Safety margin for bytecode instrumentation (JaCoCo, ASM, etc.).
31- * Instrumentation typically adds 5-15% overhead; 20% gives comfortable headroom.
32- * A method at 80% of 64KB (52,428 bytes) will still be safe after instrumentation.
36+ * Safety margin for bytecode instrumentation (JaCoCo, ASM, etc.). Instrumentation typically
37+ * adds 5-15% overhead; 20% gives comfortable headroom. A method at 80% of 64KB (52,428 bytes)
38+ * will still be safe after instrumentation.
3339 */
3440 private static final int INSTRUMENTATION_HEADROOM_PERCENT = 20 ;
3541
3642 /**
37- * Hard fail threshold: 100% minus instrumentation headroom.
38- * Methods exceeding this will likely break under JaCoCo/ASM instrumentation.
43+ * Hard fail threshold: 100% minus instrumentation headroom. Methods exceeding this will likely
44+ * break under JaCoCo/ASM instrumentation.
3945 */
4046 private static final int FAIL_PERCENT = 100 - INSTRUMENTATION_HEADROOM_PERCENT ;
4147
@@ -48,33 +54,36 @@ public class BytecodeSizeTest {
4854 (int ) (BytecodeSizeVerifier .JVM_CODE_LIMIT * (FAIL_PERCENT / 100.0 ));
4955
5056 /**
51- * The generated parser/token manager classes to check.
52- * Inner classes (e.g. CharDataConsts) are checked automatically
53- * since we scan the entire classes directory.
57+ * The generated parser/token manager classes to check. Inner classes (e.g. CharDataConsts) are
58+ * checked automatically since we scan the entire classes directory.
5459 */
5560 private static final List <String > CRITICAL_CLASSES = List .of (
5661 "CCJSqlParser" ,
57- "CCJSqlParserTokenManager"
58- );
62+ "CCJSqlParserTokenManager" );
5963
6064 // -----------------------------------------------------------------------
6165 // Test: clinit specifically (matches the reported ASM error)
6266 // -----------------------------------------------------------------------
6367
6468 /**
6569 * Reproduces the exact failure scenario:
70+ *
6671 * <pre>
6772 * org.objectweb.asm.MethodTooLargeException:
6873 * Method too large: net/sf/jsqlparser/parser/CCJSqlParserTokenManager.<clinit> ()V
6974 * </pre>
7075 *
71- * <p>Checks {@code <clinit>} in all parser-related classes (including inner classes
72- * like {@code CharDataConsts}) against the instrumentation-safe threshold.</p>
76+ * <p>
77+ * Checks {@code <clinit>} in all parser-related classes (including inner classes like
78+ * {@code CharDataConsts}) against the instrumentation-safe threshold.
79+ * </p>
7380 */
7481 @ Test
7582 void clinitMustFitWithinInstrumentationSafeLimit () throws Exception {
7683 List <BytecodeSizeVerifier .Result > results = scanParserClasses ();
77- if (results .isEmpty ()) return ; // skip if classes not found
84+ if (results .isEmpty ()) {
85+ return ; // skip if classes not found
86+ }
7887
7988 List <String > failures = new ArrayList <>();
8089
@@ -87,33 +96,37 @@ void clinitMustFitWithinInstrumentationSafeLimit() throws Exception {
8796 if (r .clinitSize > FAIL_THRESHOLD ) {
8897 failures .add (String .format (
8998 "%s.<clinit>: %,d bytes (%.1f%%) exceeds %d%% safe limit.\n "
90- + " ASM/JaCoCo instrumentation will push this over 64KB.\n "
91- + " Fix: move static array initializers to _init() methods." ,
99+ + " ASM/JaCoCo instrumentation will push this over 64KB.\n "
100+ + " Fix: move static array initializers to _init() methods." ,
92101 r .className , r .clinitSize , pct , FAIL_PERCENT ));
93102 }
94103 }
95104 }
96105
97106 assertTrue (failures .isEmpty (),
98107 "Static initializer(s) too large for safe instrumentation:\n "
99- + String .join ("\n " , failures ));
108+ + String .join ("\n " , failures ));
100109 }
101110
102111 // -----------------------------------------------------------------------
103112 // Test: all methods (general code-too-large prevention)
104113 // -----------------------------------------------------------------------
105114
106115 /**
107- * Checks every method in the generated parser and token manager classes
108- * against the instrumentation-safe threshold.
116+ * Checks every method in the generated parser and token manager classes against the
117+ * instrumentation-safe threshold.
109118 *
110- * <p>Covers: production methods, jj_3R_* lookahead scanners,
111- * jj_rescan_token, jj_la1_init_*, and all other generated methods.</p>
119+ * <p>
120+ * Covers: production methods, jj_3R_* lookahead scanners, jj_rescan_token, jj_la1_init_*, and
121+ * all other generated methods.
122+ * </p>
112123 */
113124 @ Test
114125 void allMethodsMustFitWithinInstrumentationSafeLimit () throws Exception {
115126 List <BytecodeSizeVerifier .Result > results = scanParserClasses ();
116- if (results .isEmpty ()) return ;
127+ if (results .isEmpty ()) {
128+ return ;
129+ }
117130
118131 List <String > failures = new ArrayList <>();
119132 int totalMethods = 0 ;
@@ -144,16 +157,16 @@ void allMethodsMustFitWithinInstrumentationSafeLimit() throws Exception {
144157
145158 assertTrue (failures .isEmpty (),
146159 failures .size () + " method(s) too large for safe instrumentation:\n "
147- + String .join ("\n " , failures ));
160+ + String .join ("\n " , failures ));
148161 }
149162
150163 // -----------------------------------------------------------------------
151164 // Test: named critical classes must be found
152165 // -----------------------------------------------------------------------
153166
154167 /**
155- * Ensures the critical parser classes actually exist in the build output.
156- * Catches misconfigured build paths that would silently skip all checks.
168+ * Ensures the critical parser classes actually exist in the build output. Catches misconfigured
169+ * build paths that would silently skip all checks.
157170 */
158171 @ Test
159172 void criticalClassesMustBePresent () throws Exception {
@@ -170,7 +183,7 @@ void criticalClassesMustBePresent() throws Exception {
170183 }
171184 assertTrue (found ,
172185 className + ".class not found under " + classesDir
173- + " — check build configuration" );
186+ + " — check build configuration" );
174187 }
175188 }
176189
@@ -184,7 +197,8 @@ void criticalClassesMustBePresent() throws Exception {
184197 private List <BytecodeSizeVerifier .Result > scanParserClasses () throws Exception {
185198 Path classesDir = findClassesDir ();
186199 if (classesDir == null ) {
187- System .err .println ("WARNING: compiled classes directory not found, skipping bytecode checks" );
200+ System .err .println (
201+ "WARNING: compiled classes directory not found, skipping bytecode checks" );
188202 return Collections .emptyList ();
189203 }
190204
@@ -196,8 +210,7 @@ private List<BytecodeSizeVerifier.Result> scanParserClasses() throws Exception {
196210 }
197211
198212 /**
199- * Locate the compiled classes directory.
200- * Tries standard Gradle and Maven layouts.
213+ * Locate the compiled classes directory. Tries standard Gradle and Maven layouts.
201214 */
202215 private Path findClassesDir () {
203216 // Try to infer from this test class's own location
@@ -217,14 +230,16 @@ private Path findClassesDir() {
217230 }
218231
219232 // Fallback: standard locations relative to working directory
220- for (String candidate : new String []{
233+ for (String candidate : new String [] {
221234 "build/classes/java/main" ,
222235 "target/classes" ,
223236 "build/classes/main" ,
224237 "out/production/classes"
225238 }) {
226239 Path p = Path .of (candidate );
227- if (Files .isDirectory (p )) return p ;
240+ if (Files .isDirectory (p )) {
241+ return p ;
242+ }
228243 }
229244 return null ;
230245 }
0 commit comments