This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a commit to branch fix/disable-groovy-indy-build
in repository https://gitbox.apache.org/repos/asf/grails-core.git


The following commit(s) were added to refs/heads/fix/disable-groovy-indy-build 
by this push:
     new b224f3320c fix: invoke Map constructors without Class.newInstance 
under indy-off
b224f3320c is described below

commit b224f3320cad06460bfd6ea2c046f4b802bb829f
Author: James Fredley <[email protected]>
AuthorDate: Thu Aug 20 15:48:52 2026 -0400

    fix: invoke Map constructors without Class.newInstance under indy-off
    
    Class.newInstance(Map) is not selected under @CompileStatic when
    invokedynamic is disabled. Use InvokerHelper.invokeConstructorOf with
    an explicit Object[] so nested Map-constructor types still bind.
    Also avoid `null as boolean` in the Map-constructor test fixture,
    which Groovy 5 throws on without indy after unbindable properties
    are filtered from constructor arguments.
    
    Assisted-by: Sisyphus:grok-4.6
---
 .../groovy/grails/databinding/SimpleDataBinder.groovy     | 15 ++++++++++++++-
 .../grails/web/databinding/GrailsWebDataBinderSpec.groovy |  3 ++-
 .../grails/web/databinding/GrailsWebDataBinder.groovy     |  3 ++-
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git 
a/grails-databinding-core/src/main/groovy/grails/databinding/SimpleDataBinder.groovy
 
b/grails-databinding-core/src/main/groovy/grails/databinding/SimpleDataBinder.groovy
index c84fe8b77b..33846aee05 100755
--- 
a/grails-databinding-core/src/main/groovy/grails/databinding/SimpleDataBinder.groovy
+++ 
b/grails-databinding-core/src/main/groovy/grails/databinding/SimpleDataBinder.groovy
@@ -29,6 +29,7 @@ import groovy.transform.CompileStatic
 import groovy.transform.TypeCheckingMode
 import groovy.xml.slurpersupport.GPathResult
 import org.codehaus.groovy.reflection.CachedMethod
+import org.codehaus.groovy.runtime.InvokerHelper
 
 import grails.databinding.converters.FormattedValueConverter
 import grails.databinding.converters.ValueConverter
@@ -430,12 +431,24 @@ class SimpleDataBinder implements DataBinder {
         try {
             instance = referencedType.getDeclaredConstructor().newInstance()
         } catch (NoSuchMethodException | IllegalAccessException ignored) {
-            return referencedType.newInstance(values)
+            return newInstanceFromMapArguments(referencedType, values)
         }
         bind(instance, new SimpleMapDataBindingSource(values), listener)
         instance
     }
 
+    /**
+     * Invoke a {@code Map} constructor without calling Groovy's
+     * {@code Class.newInstance(Map)}. Under {@code @CompileStatic} with
+     * invokedynamic disabled that extension is not selected, so nested
+     * objects with only a Map constructor are left unbound.
+     */
+    protected Object newInstanceFromMapArguments(Class referencedType, Map 
values) {
+        // Pass an Object[] so CompileStatic cannot treat the Map as named
+        // arguments or coerce it to a multi-arg constructor signature.
+        InvokerHelper.invokeConstructorOf(referencedType, new Object[] { 
values })
+    }
+
     @CompileStatic(TypeCheckingMode.SKIP)
     protected initializeArray(obj, String propertyName, Class arrayType, int 
index) {
         Object[] array = obj[propertyName]
diff --git 
a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy
 
b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy
index 43a1abcad9..c1b1df4b90 100644
--- 
a/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy
+++ 
b/grails-test-suite-persistence/src/test/groovy/grails/web/databinding/GrailsWebDataBinderSpec.groovy
@@ -2163,7 +2163,8 @@ class SecureMapConstructorValue implements Validateable {
 
     SecureMapConstructorValue(Map values) {
         name = values.name
-        admin = values.admin as boolean
+        // Groovy 5 without invokedynamic throws on `null as boolean`.
+        admin = Boolean.TRUE.equals(values.admin)
     }
 
     static constraints = {
diff --git 
a/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy
 
b/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy
index 7110d200fc..61cf63b31d 100644
--- 
a/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy
+++ 
b/grails-web-databinding/src/main/groovy/grails/web/databinding/GrailsWebDataBinder.groovy
@@ -641,7 +641,8 @@ class GrailsWebDataBinder extends SimpleDataBinder {
             if (value instanceof Map) {
                 if (isBindAllIncludeList(includeList) ||
                         !DataBindingUtils.isDenyByDefaultEnabled()) {
-                    return 
referencedType.newInstance(filterUnbindableMapConstructorArguments(referencedType,
 (Map) value))
+                    return newInstanceFromMapArguments(referencedType,
+                            
filterUnbindableMapConstructorArguments(referencedType, (Map) value))
                 }
                 if 
(DataBindingUtils.isGeneratedBindingIncludeList(bindingIncludeList.get())) {
                     warnAboutMissingNoArgConstructor(referencedType)

Reply via email to