polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject [08/10] polygene-java git commit: POLYGENE-120 - handle default interface methods. Article https://opencredo.com/dynamic-proxies-java-part-2/ contained the secret sauce of reflection construction of MethodHandles.Lookup instance.
Date Sun, 14 May 2017 04:08:13 GMT
POLYGENE-120 - handle default interface methods. Article https://opencredo.com/dynamic-proxies-java-part-2/
contained the secret sauce of reflection construction of MethodHandles.Lookup instance.

Signed-off-by: niclas <niclas@hedhman.org>


Project: http://git-wip-us.apache.org/repos/asf/polygene-java/repo
Commit: http://git-wip-us.apache.org/repos/asf/polygene-java/commit/ee79f220
Tree: http://git-wip-us.apache.org/repos/asf/polygene-java/tree/ee79f220
Diff: http://git-wip-us.apache.org/repos/asf/polygene-java/diff/ee79f220

Branch: refs/heads/develop
Commit: ee79f220a960f5cd484aa3163718da41ebeb55d9
Parents: 820f9d0
Author: niclas <niclas@hedhman.org>
Authored: Sat May 13 15:41:13 2017 +0800
Committer: niclas <niclas@hedhman.org>
Committed: Sun May 14 12:07:56 2017 +0800

----------------------------------------------------------------------
 .../composite/CompositeMethodsModel.java        | 92 ++++++++++++++++----
 1 file changed, 77 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ee79f220/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java
b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java
index 8dc2495..3b130ed 100644
--- a/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java
+++ b/core/runtime/src/main/java/org/apache/polygene/runtime/composite/CompositeMethodsModel.java
@@ -20,10 +20,18 @@
 
 package org.apache.polygene.runtime.composite;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.LinkedHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import org.apache.polygene.api.composite.Composite;
 import org.apache.polygene.api.composite.MissingMethodException;
 import org.apache.polygene.api.structure.ModuleDescriptor;
 import org.apache.polygene.api.util.HierarchicalVisitor;
@@ -37,6 +45,7 @@ import org.apache.polygene.runtime.injection.DependencyModel;
 public final class CompositeMethodsModel
     implements VisitableHierarchy<Object, Object>, Dependencies
 {
+    private final ConcurrentMap<Method, MethodCallHandler> methodHandleCache = new
ConcurrentHashMap<>();
     private final LinkedHashMap<Method, CompositeMethodModel> methods;
     private final MixinsModel mixinsModel;
 
@@ -58,33 +67,57 @@ public final class CompositeMethodsModel
                           Method method,
                           Object[] args,
                           ModuleDescriptor moduleInstance
-    )
+                        )
         throws Throwable
     {
         CompositeMethodModel compositeMethod = methods.get( method );
 
         if( compositeMethod == null )
         {
-            if( method.getDeclaringClass().equals( Object.class ) )
+            Class<?> declaringClass = method.getDeclaringClass();
+            if( declaringClass.equals( Object.class ) )
             {
                 return mixins.invokeObject( proxy, args, method );
             }
 
             // TODO: Figure out what was the intention of this code block, added by Rickard
in 2009. It doesn't do anything useful.
-            if( !method.getDeclaringClass().isInterface() )
+            // Update (niclas): My guess is that this is preparation for mixins in Objects.
+            if( !declaringClass.isInterface() )
             {
-                compositeMethod = mixinsModel.mixinTypes().map( aClass -> {
-                    try
-                    {
-                        Method realMethod = aClass.getMethod( method.getName(), method.getParameterTypes()
);
-                        return methods.get( realMethod );
-                    }
-                    catch( NoSuchMethodException | SecurityException e )
-                    {
-
-                    }
-                    return null;
-                }).filter( model -> model != null ).findFirst().orElse( null );
+                compositeMethod = mixinsModel.mixinTypes().map( aClass ->
+                                                                {
+                                                                    try
+                                                                    {
+                                                                        Method realMethod
= aClass.getMethod( method.getName(), method.getParameterTypes() );
+                                                                        return methods.get(
realMethod );
+                                                                    }
+                                                                    catch( NoSuchMethodException
| SecurityException e )
+                                                                    {
+
+                                                                    }
+                                                                    return null;
+                                                                } ).filter( model -> model
!= null ).findFirst().orElse( null );
+            }
+            if( method.isDefault() )
+            {
+                if( proxy instanceof Composite )
+                {
+                    MethodCallHandler callHandler = forMethod( method );
+                    return callHandler.invoke( proxy, args );
+                }
+                // Does this next line actually make any sense? Can we have a default method
on an interface where the instance is not a Composite? Maybe... Let's try to trap a usecase
by disallowing it.
+//                return method.invoke( proxy, args );
+                String message = "We have detected a default method on an interface that
is not backed by a Composite. "
+                                 + "Please report this to dev@polygene.apache.org together
with the information below, "
+                                 + "that/those class(es) and the relevant assembly information.
Thank you\nMethod:"
+                                 + method.toGenericString()
+                                 + "\nDeclaring Class:"
+                                 + method.getDeclaringClass().toGenericString()
+                                 + "\nTypes:"
+                                 + mixinsModel.mixinTypes()
+                                              .map( Class::toGenericString )
+                                              .collect( Collectors.joining( "\n" ) );
+                throw new UnsupportedOperationException( message );
             }
             throw new MissingMethodException( "Method '" + method + "' is not implemented"
);
         }
@@ -131,4 +164,33 @@ public final class CompositeMethodsModel
     {
         return methods().toString();
     }
+
+    private MethodCallHandler forMethod( Method method )
+    {
+        return methodHandleCache.computeIfAbsent( method, this::createMethodCallHandler );
+    }
+
+    private MethodCallHandler createMethodCallHandler( Method method )
+    {
+        Class<?> declaringClass = method.getDeclaringClass();
+        try
+        {
+            Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(
Class.class, int.class );
+            constructor.setAccessible( true );
+            MethodHandles.Lookup lookup = constructor.newInstance( declaringClass, MethodHandles.Lookup.PRIVATE
);
+            MethodHandle handle = lookup.unreflectSpecial( method, declaringClass );
+            return ( proxy, args ) -> handle.bindTo( proxy ).invokeWithArguments( args
);
+        }
+        catch( IllegalAccessException | NoSuchMethodException | InstantiationException |
InvocationTargetException e )
+        {
+            throw new RuntimeException( e );
+        }
+    }
+
+    @FunctionalInterface
+    private interface MethodCallHandler
+    {
+        Object invoke( Object proxy, Object[] args )
+            throws Throwable;
+    }
 }


Mime
View raw message