diff --git a/build.gradle b/build.gradle index e6e3fc4cf..a690cef6b 100644 --- a/build.gradle +++ b/build.gradle @@ -198,6 +198,79 @@ compileJavacc { ] } +// Post-process the generated CCJSqlParserTokenManager.java to split large static +// array initializers into separate methods, preventing the method from +// exceeding the JVM's 64KB bytecode limit (which breaks ASM-based tools like sbt-assembly). +tasks.register('splitTokenManagerStaticInit') { + dependsOn(compileJavacc) + + def tokenManagerFile = layout.buildDirectory.file( + "generated/javacc/net/sf/jsqlparser/parser/CCJSqlParserTokenManager.java" + ) + + inputs.file(tokenManagerFile) + outputs.file(tokenManagerFile) + + doLast { + def file = tokenManagerFile.get().asFile + if (!file.exists()) { + throw new GradleException("CCJSqlParserTokenManager.java not found at ${file}") + } + def content = file.text + + // Pattern matches static final array field declarations with inline initialization. + // We extract large ones and move their initialization into separate methods. + def fieldsToExtract = [ + // [regex-safe field name, array type for method return] + ['stringLiterals', 'int[]'], + ['jjstrLiteralImages', 'String[]'], + ['jjmatchKinds', 'int[]'], + ['jjnewLexState', 'int[]'], + ] + + fieldsToExtract.each { entry -> + def fieldName = entry[0] + def arrayType = entry[1] + + // Match: = { ... }; + // The field declaration may use 'public' or 'private' and 'static final' + def pattern = ~"(?s)((?:public|private)\\s+static\\s+final\\s+${java.util.regex.Pattern.quote(arrayType)}\\s+${fieldName}\\s*=\\s*)\\{(.*?)\\};" + def matcher = pattern.matcher(content) + if (matcher.find()) { + def prefix = matcher.group(1) + def body = matcher.group(2) + def methodName = "_init_${fieldName}" + def replacement = "${prefix}${methodName}();\n" + + " private static ${arrayType} ${methodName}() { return new ${arrayType} {${body}}; }" + content = matcher.replaceFirst(java.util.regex.Matcher.quoteReplacement(replacement)) + logger.lifecycle("splitTokenManagerStaticInit: extracted ${fieldName} initialization into ${methodName}()") + } + } + + // Handle int[][] arrays separately (jjcompositeState, jjnextStateSet) + def arrayArrayFields = ['jjcompositeState', 'jjnextStateSet'] + arrayArrayFields.each { fieldName -> + def pattern = ~"(?s)(private\\s+static\\s+final\\s+int\\[\\]\\[\\]\\s+${fieldName}\\s*=\\s*)\\{(.*?)\\};" + def matcher = pattern.matcher(content) + if (matcher.find()) { + def prefix = matcher.group(1) + def body = matcher.group(2) + def methodName = "_init_${fieldName}" + def replacement = "${prefix}${methodName}();\n" + + " private static int[][] ${methodName}() { return new int[][] {${body}}; }" + content = matcher.replaceFirst(java.util.regex.Matcher.quoteReplacement(replacement)) + logger.lifecycle("splitTokenManagerStaticInit: extracted ${fieldName} initialization into ${methodName}()") + } + } + + file.text = content + } +} + +tasks.withType(JavaCompile).configureEach { + dependsOn('splitTokenManagerStaticInit') +} + java { withSourcesJar() withJavadocJar()