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]

Reply via email to