changeset 9983:9cfb4b22a01e

8050173: Add j.l.i.MethodHandle.copyWith(MethodType, LambdaForm) Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
author vlivanov
date Wed, 10 Sep 2014 18:34:02 +0400
parents c7be76a1dda5
children d33546256199
files src/share/classes/java/lang/invoke/CallSite.java src/share/classes/java/lang/invoke/DirectMethodHandle.java src/share/classes/java/lang/invoke/MethodHandle.java src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/java/lang/invoke/MethodType.java src/share/classes/java/lang/invoke/SimpleMethodHandle.java
diffstat 7 files changed, 93 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 18:34:02 2014 +0400
@@ -242,7 +242,7 @@
             invoker = basicType.form().setCachedMethodHandle(MethodTypeForm.MH_UNINIT_CS, invoker);
         }
         // unchecked view is OK since no values will be received or returned
-        return invoker.viewAsType(targetType);
+        return invoker.viewAsType(targetType, false);
     }
 
     // unsafe stuff:
--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -127,16 +127,18 @@
     }
 
     @Override
+    MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+        assert(this.getClass() == DirectMethodHandle.class);  // must override in subclasses
+        return new DirectMethodHandle(mt, lf, member);
+    }
+
+    @Override
     String internalProperties() {
         return "\n& DMH.MN="+internalMemberName();
     }
 
     //// Implementation methods.
     @Override
-    MethodHandle viewAsType(MethodType newType) {
-        return new DirectMethodHandle(newType, form, member);
-    }
-    @Override
     @ForceInline
     MemberName internalMemberName() {
         return member;
@@ -364,8 +366,8 @@
             return true;
         }
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            return new Special(newType, form, member);
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            return new Special(mt, lf, member);
         }
     }
 
@@ -382,8 +384,8 @@
             assert(initMethod.isResolved());
         }
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            return new Constructor(newType, form, member, initMethod, instanceClass);
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            return new Constructor(mt, lf, member, initMethod, instanceClass);
         }
     }
 
@@ -412,8 +414,8 @@
             return fieldType.cast(obj);
         }
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            return new Accessor(newType, form, member, fieldOffset);
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            return new Accessor(mt, lf, member, fieldOffset);
         }
     }
 
@@ -455,8 +457,8 @@
             return fieldType.cast(obj);
         }
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            return new StaticAccessor(newType, form, member, staticBase, staticOffset);
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            return new StaticAccessor(mt, lf, member, staticBase, staticOffset);
         }
     }
 
--- a/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -1315,9 +1315,27 @@
     }
 
     /*non-public*/
-    MethodHandle viewAsType(MethodType newType) {
+    MethodHandle viewAsType(MethodType newType, boolean strict) {
         // No actual conversions, just a new view of the same method.
-        return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
+        // Note that this operation must not produce a DirectMethodHandle,
+        // because retyped DMHs, like any transformed MHs,
+        // cannot be cracked into MethodHandleInfo.
+        assert viewAsTypeChecks(newType, strict);
+        BoundMethodHandle mh = rebind();
+        assert(!((MethodHandle)mh instanceof DirectMethodHandle));
+        return mh.copyWith(newType, mh.form);
+    }
+
+    /*non-public*/
+    boolean viewAsTypeChecks(MethodType newType, boolean strict) {
+        if (strict) {
+            assert(type().isViewableAs(newType, true))
+                : Arrays.asList(this, newType);
+        } else {
+            assert(type().basicType().isViewableAs(newType.basicType(), true))
+                : Arrays.asList(this, newType);
+        }
+        return true;
     }
 
     // Decoding
@@ -1373,6 +1391,9 @@
     //// All these methods assume arguments are already validated.
 
     /*non-public*/
+    abstract MethodHandle copyWith(MethodType mt, LambdaForm lf);
+
+    /*non-public*/
     BoundMethodHandle rebind() {
         // Bind 'this' into a new invoker, of the known class BMH.
         MethodType type2 = type();
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 18:34:02 2014 +0400
@@ -84,7 +84,7 @@
             assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class);
             assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType());
             // safe to view non-strictly, because element type follows from array type
-            mh = mh.viewAsType(correctType);
+            mh = mh.viewAsType(correctType, false);
         }
         // Atomically update accessor cache.
         synchronized(cache) {
@@ -406,21 +406,24 @@
         }
 
         @Override
+        boolean viewAsTypeChecks(MethodType newType, boolean strict) {
+            super.viewAsTypeChecks(newType, true);
+            if (strict) return true;
+            // extra assertion for non-strict checks:
+            assert (type().lastParameterType().getComponentType()
+                    .isAssignableFrom(
+                            newType.lastParameterType().getComponentType()))
+                    : Arrays.asList(this, newType);
+            return true;
+        }
+
+        @Override
         MethodHandle setVarargs(MemberName member) {
             if (member.isVarargs())  return this;
             return asFixedArity();
         }
 
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            if (newType.lastParameterType() != type().lastParameterType())
-                throw new InternalError();
-            MethodHandle newTarget = asFixedArity().viewAsType(newType);
-            // put back the varargs bit:
-            return new AsVarargsCollector(newTarget, newType, arrayType);
-        }
-
-        @Override
         MemberName internalMemberName() {
             return asFixedArity().internalMemberName();
         }
@@ -434,6 +437,11 @@
         boolean isInvokeSpecial() {
             return asFixedArity().isInvokeSpecial();
         }
+
+        @Override
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            throw newIllegalArgumentException("do not use this");
+        }
     }
 
     /** Factory method:  Spread selected argument. */
@@ -996,9 +1004,10 @@
         boolean isInvokeSpecial() {
             return target.isInvokeSpecial();
         }
+
         @Override
-        MethodHandle viewAsType(MethodType newType) {
-            return new WrappedMember(target, newType, member, callerClass);
+        MethodHandle copyWith(MethodType mt, LambdaForm lf) {
+            throw newIllegalArgumentException("do not use this");
         }
     }
 
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 18:34:02 2014 +0400
@@ -1580,7 +1580,7 @@
                 return false;
             return true;
         }
-        private MethodHandle restrictReceiver(MemberName method, MethodHandle mh, Class<?> caller) throws IllegalAccessException {
+        private MethodHandle restrictReceiver(MemberName method, DirectMethodHandle mh, Class<?> caller) throws IllegalAccessException {
             assert(!method.isStatic());
             // receiver type of mh is too wide; narrow to caller
             if (!method.getDeclaringClass().isAssignableFrom(caller)) {
@@ -1589,7 +1589,9 @@
             MethodType rawType = mh.type();
             if (rawType.parameterType(0) == caller)  return mh;
             MethodType narrowType = rawType.changeParameterType(0, caller);
-            return mh.viewAsType(narrowType);
+            assert(!mh.isVarargsCollector());  // viewAsType will lose varargs-ness
+            assert(mh.viewAsTypeChecks(narrowType, true));
+            return mh.copyWith(narrowType, mh.form);
         }
 
         /** Check access and get the requested method. */
@@ -1651,15 +1653,17 @@
                 checkMethod(refKind, refc, method);
             }
 
-            MethodHandle mh = DirectMethodHandle.make(refKind, refc, method);
-            mh = maybeBindCaller(method, mh, callerClass);
-            mh = mh.setVarargs(method);
+            DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method);
+            MethodHandle mh = dmh;
             // Optionally narrow the receiver argument to refc using restrictReceiver.
             if (doRestrict &&
                    (refKind == REF_invokeSpecial ||
                        (MethodHandleNatives.refKindHasReceiver(refKind) &&
-                           restrictProtectedReceiver(method))))
-                mh = restrictReceiver(method, mh, lookupClass());
+                           restrictProtectedReceiver(method)))) {
+                mh = restrictReceiver(method, dmh, lookupClass());
+            }
+            mh = maybeBindCaller(method, mh, callerClass);
+            mh = mh.setVarargs(method);
             return mh;
         }
         private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
@@ -1691,12 +1695,12 @@
             // Optionally check with the security manager; this isn't needed for unreflect* calls.
             if (checkSecurity)
                 checkSecurityManager(refc, field);
-            MethodHandle mh = DirectMethodHandle.make(refc, field);
+            DirectMethodHandle dmh = DirectMethodHandle.make(refc, field);
             boolean doRestrict = (MethodHandleNatives.refKindHasReceiver(refKind) &&
                                     restrictProtectedReceiver(field));
             if (doRestrict)
-                mh = restrictReceiver(field, mh, lookupClass());
-            return mh;
+                return restrictReceiver(field, dmh, lookupClass());
+            return dmh;
         }
         /** Check access and get the requested constructor. */
         private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
--- a/src/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 18:34:02 2014 +0400
@@ -775,16 +775,27 @@
         return sb.toString();
     }
 
-
+    /** True if the old return type can always be viewed (w/o casting) under new return type,
+     *  and the new parameters can be viewed (w/o casting) under the old parameter types.
+     */
     /*non-public*/
-    boolean isViewableAs(MethodType newType) {
-        if (!VerifyType.isNullConversion(returnType(), newType.returnType(), true))
+    boolean isViewableAs(MethodType newType, boolean keepInterfaces) {
+        if (!VerifyType.isNullConversion(returnType(), newType.returnType(), keepInterfaces))
             return false;
+        return parametersAreViewableAs(newType, keepInterfaces);
+    }
+    /** True if the new parameters can be viewed (w/o casting) under the old parameter types. */
+    /*non-public*/
+    boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces) {
+        if (form == newType.form && form.erasedType == this)
+            return true;  // my reference parameters are all Object
+        if (ptypes == newType.ptypes)
+            return true;
         int argc = parameterCount();
         if (argc != newType.parameterCount())
             return false;
         for (int i = 0; i < argc; i++) {
-            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), true))
+            if (!VerifyType.isNullConversion(newType.parameterType(i), parameterType(i), keepInterfaces))
                 return false;
         }
         return true;
--- a/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
+++ b/src/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 18:34:02 2014 +0400
@@ -37,4 +37,9 @@
     /*non-public*/ static SimpleMethodHandle make(MethodType type, LambdaForm form) {
         return new SimpleMethodHandle(type, form);
     }
+
+    @Override
+    /*non-public*/ SimpleMethodHandle copyWith(MethodType mt, LambdaForm lf) {
+        return make(mt, lf);
+    }
 }