codeconsole opened a new pull request, #16377:
URL: https://github.com/apache/grails-core/pull/16377
## Problem
A page that compiled under 8.0.0-M6 stops the build under 8.0.0-RC1:
```
> Task :compileGroovyPages FAILED
java.util.concurrent.ExecutionException: java.lang.StackOverflowError
at
org.grails.gsp.compiler.GroovyPageCompiler.compile(GroovyPageCompiler.groovy:147)
Caused by: java.lang.StackOverflowError
at java.util.regex.Pattern$Bound.check
at java.util.regex.Pattern$LazyLoop.match
at java.util.regex.Pattern$GroupTail.match
at java.util.regex.Pattern$BranchConn.match
at java.util.regex.Pattern$CharProperty.match
at java.util.regex.Pattern$Branch.match
at java.util.regex.Pattern$GroupHead.match
at java.util.regex.Pattern$LazyLoop.match
... the same block, repeated
```
The pattern is `GroovyPageParser.PAGE_SCOPE_VARIABLE_PATTERN`, added in
#16139 to collect the `var` and `status` attributes a statically compiled page
may read:
```
<\w+:(?:[^>"']|"[^"]*"|'[^']*')*?\b(?:var|status)\s*=\s*["']([A-Za-z_$][\w$]*)["']
```
It pairs the quotes of an attribute without knowing what a `${...}`
expression is. This is ordinary GSP:
```gsp
<meta property="og:title" content="${t ?: 'Untitled'.replaceAll('"','\'')}"
/>
```
It is scanned **after** the layout preprocessor has rewritten the `<meta>`
into a namespaced `<grailsLayout:captureMeta ...>` tag. The `"` inside `'"'`
closes the attribute as far as the pattern can tell. The page's later quotes
then pair up across tags and swallow the `>` that should end the attempt, and
the lazy loop recurses once per character through the rest of the page until
the thread runs out of stack.
It looks intermittent because it depends on frame size. With the JIT warm, a
moderate page survives, so the page compiled on a macOS development machine (2
MB default thread stack). It failed on a Linux x64 CI runner, where the default
is 1 MB and the code is still largely interpreted early in the build.
## Fix
`PageScopeVariableScanner` reads the same thing in one forward pass. It:
- finds a namespaced tag;
- steps over each `${...}` expression as a unit, counting braces and
stepping over Groovy string literals, so a quote or brace inside one ends
nothing;
- records a `var` or `status` whose value is a quoted identifier.
The work is linear in the page, and the call stack stays shallow whatever
the page contains. Well-formed tags yield the same names as before, so the
static compilation this feeds is unchanged, including the "errs towards
matching" behaviour documented on the original pattern.
## Tests
- **`PageScopeVariableScannerSpec`** (new) covers:
- the tag shapes from #16139's own spec cases, plus single quotes and
whitespace around `=`;
- that only namespaced tags count;
- that non-identifier values introduce nothing;
- that `var=` inside another attribute's value is not an attribute;
- the og:title line in the preprocessed form the parser actually scans;
- expressions holding closures and braces inside strings;
- a 5,000-line page on a 256 KB thread.
- **`GspCompileStaticConfigSpec`** gets an end-to-end case. It compiles,
through the real engine and layout preprocessing, a page shaped like the one
that failed, on a 256 KB thread, and checks that the name introduced at the
very end is still found. **Against the previous parser this test fails with the
same `StackOverflowError`**; with this change it passes.
`:grails-gsp-core:test` (255 tests), `:grails-gsp-core:codeStyle` and `rat`
pass.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]