8050173: Add j.l.i.MethodHandle.copyWith(MethodType, LambdaForm)
authorvlivanov
Wed, 10 Sep 2014 19:19:49 +0400
changeset 26470 1586df597397
parent 26469 e6bc14fae1cf
child 26471 1de6be0c6945
8050173: Add j.l.i.MethodHandle.copyWith(MethodType, LambdaForm) Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com
jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java
jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/CallSite.java	Wed Sep 10 19:19:49 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/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Sep 10 19:19:49 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Wed Sep 10 19:19:49 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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 10 19:19:49 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,18 +406,21 @@
         }
 
         @Override
-        MethodHandle setVarargs(MemberName member) {
-            if (member.isVarargs())  return this;
-            return asFixedArity();
+        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 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);
+        MethodHandle setVarargs(MemberName member) {
+            if (member.isVarargs())  return this;
+            return asFixedArity();
         }
 
         @Override
@@ -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/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 10 19:19:49 2014 +0400
@@ -1579,7 +1579,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)) {
@@ -1588,7 +1588,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. */
@@ -1650,15 +1652,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,
@@ -1690,12 +1694,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/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Wed Sep 10 19:19:49 2014 +0400
@@ -773,16 +773,27 @@
         return sj.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/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 19:19:48 2014 +0400
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/SimpleMethodHandle.java	Wed Sep 10 19:19:49 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);
+    }
 }