8050173: Add j.l.i.MethodHandle.copyWith(MethodType, LambdaForm)
Reviewed-by: vlivanov, psandoz
Contributed-by: john.r.rose@oracle.com
--- 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);
+ }
}