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]