You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/03/13 10:43:30 UTC

[14/25] incubator-freemarker git commit: Fixed problem in default method support, where the bridge method and the related reified method appears as overloaded methods with same parameter types, if the reified method comes from a default method, and the b

Fixed problem in default method support, where the bridge method and the related reified method appears as overloaded methods with same parameter types, if the reified method comes from a default method, and the bridge method comes from a class.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/d095f5ae
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/d095f5ae
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/d095f5ae

Branch: refs/heads/2.3
Commit: d095f5ae38b0bdefa87a5d732aceb9acb6997581
Parents: cbc4495
Author: ddekany <dd...@apache.org>
Authored: Sun Mar 12 13:42:59 2017 +0100
Committer: ddekany <dd...@apache.org>
Committed: Sun Mar 12 13:42:59 2017 +0100

----------------------------------------------------------------------
 .../freemarker/ext/beans/ClassIntrospector.java | 72 +++++++++++++++-----
 .../beans/BeansWrapperBridgeMethodsTest.java    | 47 +++++++++++++
 .../freemarker/ext/beans/BridgeMethodsBean.java | 12 ++++
 .../ext/beans/BridgeMethodsBeanBase.java        | 11 +++
 .../BridgeMethodsWithDefaultMethodBean.java     | 11 +++
 .../BridgeMethodsWithDefaultMethodBean2.java    |  5 ++
 .../BridgeMethodsWithDefaultMethodBeanBase.java | 13 ++++
 ...BridgeMethodsWithDefaultMethodBeanBase2.java | 10 +++
 8 files changed, 163 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/main/java/freemarker/ext/beans/ClassIntrospector.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index 8fa0037..911b14d 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -597,25 +597,47 @@ class ClassIntrospector {
             // java.beans.Introspector was good enough then.
             return introspectionMDs;
         }
-        
-        boolean anyDefaultMethodsAdded = false;
-        findDefaultMethods: for (Method method : clazz.getMethods()) {
-            if (_JavaVersions.JAVA_8.isDefaultMethod(method)) {
-                if (!anyDefaultMethodsAdded) {
-                    for (MethodDescriptor methodDescriptor : introspectionMDs) {
-                        // Check if java.bean.Introspector now finds default methods (it did not in Java 1.8.0_66):
-                        if (_JavaVersions.JAVA_8.isDefaultMethod(methodDescriptor.getMethod())) {
-                            break findDefaultMethods;
-                        }
-                        
-                        // Recreate introspectionMDs so that its size can grow: 
-                        ArrayList<MethodDescriptor> newIntrospectionMDs
-                                = new ArrayList<MethodDescriptor>(introspectionMDs.size() + 16);
-                        newIntrospectionMDs.addAll(introspectionMDs);
-                        introspectionMDs = newIntrospectionMDs;
-                    }
-                    anyDefaultMethodsAdded = true;
+
+        Map<String, List<Method>> defaultMethodsToAddByName = null;
+        for (Method method : clazz.getMethods()) {
+            if (_JavaVersions.JAVA_8.isDefaultMethod(method) && !method.isBridge()) {
+                if (defaultMethodsToAddByName == null) {
+                    defaultMethodsToAddByName = new HashMap<String, List<Method>>();
+                }
+                List<Method> overloads = defaultMethodsToAddByName.get(method.getName());
+                if (overloads == null) {
+                    overloads = new ArrayList<Method>(0);
+                    defaultMethodsToAddByName.put(method.getName(), overloads);
                 }
+                overloads.add(method);
+            }
+        }
+        
+        if (defaultMethodsToAddByName == null) {
+            // We had no interfering default methods:
+            return introspectionMDs;
+        }
+
+        // Recreate introspectionMDs so that its size can grow: 
+        ArrayList<MethodDescriptor> newIntrospectionMDs
+                = new ArrayList<MethodDescriptor>(introspectionMDs.size() + 16);
+        for (MethodDescriptor introspectorMD : introspectionMDs) {
+            Method introspectorM = introspectorMD.getMethod();
+            // Prevent cases where the same method is added with different return types both from the list of default
+            // methods and from the list of Introspector-discovered methods, as that would lead to overloaded method
+            // selection ambiguity later. This is known to happen when the default method in an interface has reified
+            // return type, and then the interface is implemented by a class where the compiler generates an override
+            // for the bridge method only. (Other tricky cases might exist.)
+            if (!containsMethodWithSameParameterTypes(
+                    defaultMethodsToAddByName.get(introspectorM.getName()), introspectorM)) {
+                newIntrospectionMDs.add(introspectorMD);
+            }
+        }
+        introspectionMDs = newIntrospectionMDs;
+        
+        // Add default methods:
+        for (Entry<String, List<Method>> entry : defaultMethodsToAddByName.entrySet()) {
+            for (Method method : entry.getValue()) {
                 introspectionMDs.add(new MethodDescriptor(method));
             }
         }
@@ -623,6 +645,20 @@ class ClassIntrospector {
         return introspectionMDs;
     }
 
+    private boolean containsMethodWithSameParameterTypes(List<Method> overloads, Method m) {
+        if (overloads == null) {
+            return false;
+        }
+        
+        Class<?>[] paramTypes = m.getParameterTypes();
+        for (Method overload : overloads) {
+            if (Arrays.equals(overload.getParameterTypes(), paramTypes)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void addPropertyDescriptorToClassIntrospectionData(Map<Object, Object> introspData,
             PropertyDescriptor pd, Class<?> clazz, Map<MethodSignature, List<Method>> accessibleMethods) {
         if (pd instanceof IndexedPropertyDescriptor) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BeansWrapperBridgeMethodsTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BeansWrapperBridgeMethodsTest.java b/src/test/java/freemarker/ext/beans/BeansWrapperBridgeMethodsTest.java
new file mode 100644
index 0000000..5f73d0e
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BeansWrapperBridgeMethodsTest.java
@@ -0,0 +1,47 @@
+package freemarker.ext.beans;
+
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+
+import org.junit.Test;
+
+import freemarker.template.Configuration;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateMethodModelEx;
+import freemarker.template.TemplateModelException;
+
+public class BeansWrapperBridgeMethodsTest {
+    
+    @Test
+    public void testWithoutDefaultMethod() throws TemplateModelException {
+        test(BridgeMethodsBean.class);
+    }
+
+    @Test
+    public void testWithDefaultMethod() throws TemplateModelException {
+        test(BridgeMethodsWithDefaultMethodBean.class);
+    }
+
+    @Test
+    public void testWithDefaultMethod2() throws TemplateModelException {
+        test(BridgeMethodsWithDefaultMethodBean2.class);
+    }
+
+    private void test(Class<?> pClass) throws TemplateModelException {
+        BeansWrapper ow = new BeansWrapperBuilder(Configuration.VERSION_2_3_26).build();
+        TemplateHashModel wrapped;
+        try {
+            wrapped = (TemplateHashModel) ow.wrap(pClass.newInstance());
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+        
+        TemplateMethodModelEx m1 = (TemplateMethodModelEx) wrapped.get("m1");
+        assertEquals(BridgeMethodsBean.M1_RETURN_VALUE, "" + m1.exec(Collections.emptyList()));
+        
+        TemplateMethodModelEx m2 = (TemplateMethodModelEx) wrapped.get("m2");
+        assertNull(m2.exec(Collections.emptyList()));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsBean.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsBean.java b/src/test/java/freemarker/ext/beans/BridgeMethodsBean.java
new file mode 100644
index 0000000..a32853c
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsBean.java
@@ -0,0 +1,12 @@
+package freemarker.ext.beans;
+
+public class BridgeMethodsBean extends BridgeMethodsBeanBase<String> {
+
+    static final String M1_RETURN_VALUE = "m1ReturnValue"; 
+    
+    @Override
+    public String m1() {
+        return M1_RETURN_VALUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsBeanBase.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsBeanBase.java b/src/test/java/freemarker/ext/beans/BridgeMethodsBeanBase.java
new file mode 100644
index 0000000..4823400
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsBeanBase.java
@@ -0,0 +1,11 @@
+package freemarker.ext.beans;
+
+public abstract class BridgeMethodsBeanBase<T> {
+
+    public abstract T m1();
+    
+    public T m2() {
+        return null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean.java b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean.java
new file mode 100644
index 0000000..0c18991
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean.java
@@ -0,0 +1,11 @@
+package freemarker.ext.beans;
+
+public class BridgeMethodsWithDefaultMethodBean implements BridgeMethodsWithDefaultMethodBeanBase<String> {
+
+    static final String M1_RETURN_VALUE = "m1ReturnValue"; 
+    
+    public String m1() {
+        return M1_RETURN_VALUE;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean2.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean2.java b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean2.java
new file mode 100644
index 0000000..85bc232
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBean2.java
@@ -0,0 +1,5 @@
+package freemarker.ext.beans;
+
+public class BridgeMethodsWithDefaultMethodBean2 implements BridgeMethodsWithDefaultMethodBeanBase2 {
+    // All inherited
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase.java b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase.java
new file mode 100644
index 0000000..68960de
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase.java
@@ -0,0 +1,13 @@
+package freemarker.ext.beans;
+
+public interface BridgeMethodsWithDefaultMethodBeanBase<T> {
+
+    default T m1() {
+        return null;
+    }
+    
+    default T m2() {
+        return null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/d095f5ae/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase2.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase2.java b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase2.java
new file mode 100644
index 0000000..be46d4f
--- /dev/null
+++ b/src/test/java/freemarker/ext/beans/BridgeMethodsWithDefaultMethodBeanBase2.java
@@ -0,0 +1,10 @@
+package freemarker.ext.beans;
+
+public interface BridgeMethodsWithDefaultMethodBeanBase2 extends BridgeMethodsWithDefaultMethodBeanBase<String> {
+
+    @Override
+    default String m1() {
+        return BridgeMethodsWithDefaultMethodBean.M1_RETURN_VALUE;
+    }
+    
+}