Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: split CCJSqlParserTokenManager static initializers to avoid 64KB…
… method limit

The generated CCJSqlParserTokenManager class has a clinit method that reaches
64,452 bytes -- dangerously close to the JVM 65,535 byte limit. ASM-based tools
like sbt-assembly add transformation overhead that pushes it over, causing
MethodTooLargeException during fat JAR creation.

Add a splitTokenManagerStaticInit Gradle task that post-processes the generated
Java file after JavaCC code generation, extracting 6 large static array
initializations (stringLiterals, jjstrLiteralImages, jjmatchKinds, jjnewLexState,
jjcompositeState, jjnextStateSet) into separate private static methods. This
reduces clinit from 64,452 bytes to 404 bytes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
  • Loading branch information
hayssams and claude committed Apr 8, 2026
commit 6373b82a70c561f61eb6b7d6d7db050d8cb39119
73 changes: 73 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,79 @@ compileJavacc {
]
}

// Post-process the generated CCJSqlParserTokenManager.java to split large static
// array initializers into separate methods, preventing the <clinit> 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: <modifiers> <type> <fieldName> = { ... };
// 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()
Expand Down