codeconsole opened a new pull request, #16139:
URL: https://github.com/apache/grails-core/pull/16139
### Summary
GSP has been able to compile pages statically for a long time, but the way
to ask for it across an application was undocumented, untested, and failed on
ordinary correct GSP. This makes it something an application can actually turn
on.
### Compiling every page statically
The `grails.views.gsp.compileStatic` setting already existed and was already
read by both the build's page compiler and the application, but appeared
nowhere in the guide and had no test covering it — every existing case drove
static compilation from the per-page directive:
```yaml
grails:
views:
gsp:
compileStatic: true
```
The same thing can now be asked for from the build, alongside the artefact
opt-ins:
```groovy
grails {
compileStatic {
gsp = true
}
}
```
This states the setting that already exists rather than introducing a name
of its own, as a system property, so it reaches both places a page is compiled
— the forked compiler the build runs, and the JVM running the application,
which compiles a page again when it changes. Reaching only the first would mean
a page compiled one way while being developed and another when packaged.
An individual page still decides for itself:
```html
<%@ page compileStatic="false" %>
```
### Reading the names the framework supplies
A statically compiled page resolved a name it had not declared through
`getProperty`, typed `Object`, so this failed to compile:
```html
${params.id}
```
```
[Static type checking] - No such property: id for class: java.lang.Object
```
That is idiomatic GSP, and it applied equally to `flash`, `controllerName`,
`actionName`, `namespace`, `grailsApplication` and `applicationContext` — names
a page never declares because the framework binds them. They are now typed, so
a page can read them without declaring anything:
```html
<%@ page compileStatic="true" %>
<g:if test="${params.id}">${flash.message} on
${controllerName}/${actionName}</g:if>
```
`params` and `flash` are typed `Map`, which is what lets the type checker
turn `params.id` into a `get()`. Each delegates to the same binding lookup
dynamic resolution uses, so a page renders identically and reads `null` where
it is rendered outside a web request.
### Holding an artefact type back from `all`
`compileStatic { all = true; services = false }` enabled services anyway:
the shortcut was folded in as `all || services`, and every flag carried a
convention of `false`, so a type set to `false` could not be told apart from
one never set. An artefact type now falls back to `all` rather than being OR-ed
with it, so a value stated for a type is the value used:
```groovy
grails {
compileStatic {
all = true
services = false // every artefact type except services
}
}
```
### Limitations
- **`gsp` is not included in `all`, deliberately.** The artefact opt-ins
fail on code that is doubtful anyway. This one fails on any page reading a
model variable it has not declared, which describes most pages in an
application that has never declared one. Enabling it is a migration, not a
switch, and it should not happen as a side effect of asking for everything.
- **`request`, `response`, `session` and `application` are still not
typed.** They are servlet types, and `grails-gsp-core` deliberately has no
servlet dependency — pages render outside a servlet container too. A page can
declare one in its `model` directive to read it, which the tests pin using a
test-only servlet-api dependency. Typing them properly means a servlet-aware
page base class in `grails-web-gsp` and selecting it by name rather than by
class literal, which is more indirection than four names justify on their own.
- **`<g:set var="x" .../>` followed by `${x}` does not compile.** `g:set`
declares nothing the type checker can see; `<g:def type="..." var="..."
value="${...}"/>` is the typed form. Unchanged here, and worth addressing
separately.
- **The build option outranks `application.yml`** rather than acting as a
default under it, matching the order a system property takes over configuration
in a running application. The alternative — the build and the running
application disagreeing about precedence — is worse. It does mean a project
setting `gsp = true` in the build cannot turn it off per-environment in
configuration. Set it in one place or the other.
- **Templates.** A `_foo.gsp` rendered via `<g:render template="foo"
model="[...]"/>` takes its model from the caller, so it has to declare every
attribute it may be passed, and nothing checks that the caller and the template
agree.
### Relationship to #16134
#16134 resolves tag calls at compile time, and overlaps with this in one
place. Its change to `GroovyPageTypeCheckingExtension` seeds the allowed
namespaces from the compiled tag library index:
```groovy
currentScope.allowedTagLibs.addAll(tagLibraryIndex.namespaces)
currentScope.allowedTagLibs.addAll(tagLibraryIndex.dynamicNamespaces)
```
Once that merges:
- **`grails.views.gsp.compileStaticConfig.taglibs` and the `taglibs` page
directive stop being needed** for any tag library on the compile classpath —
its namespaces are known rather than declared. The section documenting them
here should gain a note saying so; they remain meaningful only for namespaces
filled in while the application runs.
- **A misspelled tag becomes a compile error** under that PR's `strictTags`,
rather than a runtime failure — a strictly better guarantee than the allowlist
this documents.
- Nothing else here changes. #16134 touches tag dispatch only: its
`GroovyPage` change is `getTagLibraryLookup()` and `methodMissing`,
`resolveProperty` is untouched, and `<g:set>` is listed under its own "what is
not rewritten".
Two files are touched by both and will need merging whichever lands second —
`GrailsCompileStaticOptions` (that PR adds `strictTags` and
`dynamicTagNamespaces` to the same block this adds `gsp` to, and this changes
the conventions the existing flags carry) and `GroovyPagePlugin`.
--
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]