jamesfredley opened a new issue, #15506:
URL: https://github.com/apache/grails-core/issues/15506

   ## Summary
   
   Tags defined in `RenderGrailsLayoutTagLib` (e.g., `pageProperty`, 
`layoutTitle`, `layoutBody`) cannot be called as bare methods in GSP expression 
syntax (`${pageProperty(name: 'meta.description')}`). They work correctly with 
the explicit `g.` namespace prefix (`${g.pageProperty(name: 
'meta.description')}`).
   
   The root cause is that `RenderGrailsLayoutTagLib` has `@CompileStatic` at 
class level, and `DefaultGrailsTagLibClass` tag discovery does not find 
`Closure`-typed properties on `@CompileStatic` classes.
   
   ## Version
   
   Grails 7.0.x (all versions since 7.0.0-M5, when `RenderGrailsLayoutTagLib` 
was introduced). Tested on 7.0.9-SNAPSHOT.
   
   ## Steps to Reproduce
   
   1. Create a layout GSP (e.g., `layouts/marketing.gsp`)
   2. Use `pageProperty` as a bare method call in a GSP expression:
      ```gsp
      <meta name="description" content="${pageProperty(name: 
'meta.description') ?: 'default'}"/>
      ```
   3. Visit a page that uses this layout
   
   **Expected:** The page renders normally, `pageProperty` resolves to the tag 
from `RenderGrailsLayoutTagLib`.
   
   **Actual:** 500 error: `No signature of method: 
...marketing_gsp.pageProperty() is applicable for argument types: 
(LinkedHashMap)`
   
   ## Workaround
   
   Using the explicit `g.` namespace prefix works:
   
   ```gsp
   <meta name="description" content="${g.pageProperty(name: 'meta.description') 
?: 'default'}"/>
   ```
   
   This is because `g.pageProperty()` goes through 
`GroovyPage.getProperty("g")` -> `NamespacedTagDispatcher` -> 
`TagLibraryLookup.lookupTagLibrary()`, which is a different resolution path 
that works correctly.
   
   ## Root Cause Analysis
   
   ### Tag discovery mechanism
   
   `DefaultGrailsTagLibClass` (line 62-71) discovers tags by iterating 
metaclass properties:
   
   ```java
   for (MetaProperty prop : 
GroovySystem.getMetaClassRegistry().getMetaClass(clazz).getProperties()) {
       int modifiers = prop.getModifiers();
       if (Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
           continue;
       }
       if (Closure.class.isAssignableFrom(prop.getType())) {
           tags.add(prop.getName());
       }
   }
   ```
   
   When a TagLib class has `@CompileStatic`, Groovy 4 compiles the `Closure` 
property fields differently. The metaclass property type no longer reports as 
`Closure`, so `Closure.class.isAssignableFrom(prop.getType())` returns false, 
and the tags are not registered.
   
   ### Two resolution paths
   
   | Call syntax | Resolution path | Works? |
   |---|---|---|
   | `g.pageProperty(name: ...)` | `GroovyPage.getProperty("g")` -> 
`NamespacedTagDispatcher.methodMissing` -> 
`TagLibraryLookup.lookupTagLibrary("g", "pageProperty")` | Yes |
   | `pageProperty(name: ...)` | `ExpandoMetaClass.methodMissing` -> 
`TagLibraryMetaUtils.methodMissingForTagLib` -> 
`TagLibraryLookup.lookupTagLibrary("g", "pageProperty")` | No |
   
   Both paths use the same `TagLibraryLookup`. The bare method path fails 
because the tag was never registered in the lookup's `tagNamespaces` map (due 
to the property scanning issue above). The `g.` path works because the 
`NamespacedTagDispatcher` has its own `methodMissing` that still finds the bean.
   
   ### Confirmed by test
   
   Locally removed `@CompileStatic` from `RenderGrailsLayoutTagLib`, published 
to mavenLocal, and tested: **bare `pageProperty()` works correctly after 
removal**. Re-adding `@CompileStatic` breaks it again.
   
   ### Affected TagLib
   
   `RenderGrailsLayoutTagLib` in `grails-layout` module is the only framework 
TagLib with class-level `@CompileStatic`. Other built-in TagLibs (e.g., 
`ApplicationTagLib`, `FormTagLib`, `RenderTagLib`) do not have class-level 
`@CompileStatic` and their tags work as bare methods.
   
   ### History
   
   `RenderGrailsLayoutTagLib` was introduced in commit `0be9f5d486` ("revert 
sitemesh3 upgrade & rename sitemesh -> layout") included in 7.0.0-M5. The 
class-level `@CompileStatic` was present from its creation. The prior 
`RenderSitemeshTagLib` (Sitemesh 3, since reverted) did not have 
`@CompileStatic`.
   
   ## Affected tags
   
   All tags in `RenderGrailsLayoutTagLib`:
   - `pageProperty`
   - `ifPageProperty`
   - `layoutTitle`
   - `layoutHead`
   - `layoutBody`
   - `applyLayout`
   - `content`
   
   ## Note on documentation
   
   The [official pageProperty 
docs](https://grails.apache.org/docs/7.0.0/ref/Tags%20-%20GSP/pageProperty.html)
 show `${pageProperty(name:'body.onload')}` without the `g.` prefix, which does 
not work due to this bug.


-- 
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