Merge
diff --git a/makefiles/profile-rtjar-includes.txt b/makefiles/profile-rtjar-includes.txt
index 4722727..4cc528e 100644
--- a/makefiles/profile-rtjar-includes.txt
+++ b/makefiles/profile-rtjar-includes.txt
@@ -349,6 +349,7 @@
com/sun/rowset/providers \
com/sun/script/javascript \
com/sun/script/util \
+ com/sun/security/auth \
com/sun/security/auth/callback \
com/sun/security/auth/login \
com/sun/security/auth/module \
@@ -448,8 +449,7 @@
sun/tracing \
sun/tracing/dtrace
-PROFILE_3_RTJAR_INCLUDE_TYPES := \
- com/sun/security/auth/*.class
+PROFILE_3_RTJAR_INCLUDE_TYPES :=
PROFILE_3_RTJAR_EXCLUDE_TYPES := \
javax/management/remote/rmi/_RMIConnectionImpl_Tie.class \
@@ -457,10 +457,10 @@
javax/management/remote/rmi/_RMIServerImpl_Tie.class \
javax/management/remote/rmi/_RMIServer_Stub.class \
com/sun/security/auth/callback/DialogCallbackHandler.class \
- com/sun/security/auth/callback/DialogCallbackHandler\$$$$1.class \
- com/sun/security/auth/callback/DialogCallbackHandler\$$$$2.class \
- com/sun/security/auth/callback/DialogCallbackHandler\$$$$Action.class \
- com/sun/security/auth/callback/DialogCallbackHandler\$$$$ConfirmationInfo.class
+ com/sun/security/auth/callback/DialogCallbackHandler\$$1.class \
+ com/sun/security/auth/callback/DialogCallbackHandler\$$2.class \
+ com/sun/security/auth/callback/DialogCallbackHandler\$$Action.class \
+ com/sun/security/auth/callback/DialogCallbackHandler\$$ConfirmationInfo.class
PROFILE_3_INCLUDE_METAINF_SERVICES := \
META-INF/services/javax.script.ScriptEngineFactory
diff --git a/src/share/classes/java/lang/invoke/DirectMethodHandle.java b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
index db8d35f..7793bc2 100644
--- a/src/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/src/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -65,8 +65,7 @@
}
// Factory methods:
-
- static DirectMethodHandle make(Class<?> receiver, MemberName member) {
+ static DirectMethodHandle make(byte refKind, Class<?> receiver, MemberName member) {
MethodType mtype = member.getMethodOrFieldType();
if (!member.isStatic()) {
if (!member.getDeclaringClass().isAssignableFrom(receiver) || member.isConstructor())
@@ -74,8 +73,14 @@
mtype = mtype.insertParameterTypes(0, receiver);
}
if (!member.isField()) {
- LambdaForm lform = preparedLambdaForm(member);
- return new DirectMethodHandle(mtype, lform, member);
+ if (refKind == REF_invokeSpecial) {
+ member = member.asSpecial();
+ LambdaForm lform = preparedLambdaForm(member);
+ return new Special(mtype, lform, member);
+ } else {
+ LambdaForm lform = preparedLambdaForm(member);
+ return new DirectMethodHandle(mtype, lform, member);
+ }
} else {
LambdaForm lform = preparedFieldLambdaForm(member);
if (member.isStatic()) {
@@ -89,6 +94,12 @@
}
}
}
+ static DirectMethodHandle make(Class<?> receiver, MemberName member) {
+ byte refKind = member.getReferenceKind();
+ if (refKind == REF_invokeSpecial)
+ refKind = REF_invokeVirtual;
+ return make(refKind, receiver, member);
+ }
static DirectMethodHandle make(MemberName member) {
if (member.isConstructor())
return makeAllocator(member);
@@ -124,6 +135,10 @@
//// Implementation methods.
@Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new DirectMethodHandle(newType, form, member);
+ }
+ @Override
@ForceInline
MemberName internalMemberName() {
return member;
@@ -367,6 +382,21 @@
((DirectMethodHandle)mh).ensureInitialized();
}
+ /** This subclass represents invokespecial instructions. */
+ static class Special extends DirectMethodHandle {
+ private Special(MethodType mtype, LambdaForm form, MemberName member) {
+ super(mtype, form, member);
+ }
+ @Override
+ boolean isInvokeSpecial() {
+ return true;
+ }
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new Special(newType, form, member);
+ }
+ }
+
/** This subclass handles constructor references. */
static class Constructor extends DirectMethodHandle {
final MemberName initMethod;
@@ -379,6 +409,10 @@
this.instanceClass = instanceClass;
assert(initMethod.isResolved());
}
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new Constructor(newType, form, member, initMethod, instanceClass);
+ }
}
/*non-public*/ static Object constructorMethod(Object mh) {
@@ -405,6 +439,10 @@
@Override Object checkCast(Object obj) {
return fieldType.cast(obj);
}
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new Accessor(newType, form, member, fieldOffset);
+ }
}
@ForceInline
@@ -444,6 +482,10 @@
@Override Object checkCast(Object obj) {
return fieldType.cast(obj);
}
+ @Override
+ MethodHandle viewAsType(MethodType newType) {
+ return new StaticAccessor(newType, form, member, staticBase, staticOffset);
+ }
}
@ForceInline
diff --git a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
index f2bb8eb..f7191f7 100644
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
+++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java
@@ -53,7 +53,7 @@
private static final String NAME_OBJECT = "java/lang/Object";
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
= MethodType.methodType(void.class,
- String.class,
+ Class.class,
int.class, String.class, String.class, String.class,
int.class, String.class, String.class, String.class,
String.class,
@@ -284,7 +284,7 @@
mv.visitCode();
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
mv.visitInsn(DUP);;
- mv.visitLdcInsn(targetClass.getName().replace('.', '/'));
+ mv.visitLdcInsn(Type.getType(targetClass));
mv.visitLdcInsn(samInfo.getReferenceKind());
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
mv.visitLdcInsn(samInfo.getName());
diff --git a/src/share/classes/java/lang/invoke/MethodHandle.java b/src/share/classes/java/lang/invoke/MethodHandle.java
index aa323eb..774faff 100644
--- a/src/share/classes/java/lang/invoke/MethodHandle.java
+++ b/src/share/classes/java/lang/invoke/MethodHandle.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1250,8 +1250,6 @@
/*non-public*/
MethodHandle viewAsType(MethodType newType) {
// No actual conversions, just a new view of the same method.
- if (!type.isViewableAs(newType))
- throw new InternalError();
return MethodHandleImpl.makePairwiseConvert(this, newType, 0);
}
@@ -1268,6 +1266,11 @@
}
/*non-public*/
+ boolean isInvokeSpecial() {
+ return false; // DMH.Special returns true
+ }
+
+ /*non-public*/
Object internalValues() {
return null;
}
diff --git a/src/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
index 1b35e0f..a18a7a4 100644
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -367,11 +367,11 @@
@Override
MethodHandle viewAsType(MethodType newType) {
- MethodHandle mh = super.viewAsType(newType);
+ if (newType.lastParameterType() != type().lastParameterType())
+ throw new InternalError();
+ MethodHandle newTarget = asFixedArity().viewAsType(newType);
// put back the varargs bit:
- MethodType type = mh.type();
- int arity = type.parameterCount();
- return mh.asVarargsCollector(type.parameterType(arity-1));
+ return new AsVarargsCollector(newTarget, newType, arrayType);
}
@Override
@@ -379,6 +379,12 @@
return asFixedArity().internalMemberName();
}
+ /*non-public*/
+ @Override
+ boolean isInvokeSpecial() {
+ return asFixedArity().isInvokeSpecial();
+ }
+
@Override
MethodHandle bindArgument(int pos, char basicType, Object value) {
diff --git a/src/share/classes/java/lang/invoke/MethodHandleInfo.java b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
index ae579ab..380ca59 100644
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java
+++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,7 +32,6 @@
*/
final class MethodHandleInfo {
public static final int
- REF_NONE = Constants.REF_NONE,
REF_getField = Constants.REF_getField,
REF_getStatic = Constants.REF_getStatic,
REF_putField = Constants.REF_putField,
@@ -48,12 +47,17 @@
private final MethodType methodType;
private final int referenceKind;
- public MethodHandleInfo(MethodHandle mh) throws ReflectiveOperationException {
+ public MethodHandleInfo(MethodHandle mh) {
MemberName mn = mh.internalMemberName();
+ if (mn == null) throw new IllegalArgumentException("not a direct method handle");
this.declaringClass = mn.getDeclaringClass();
this.name = mn.getName();
- this.methodType = mn.getMethodType();
- this.referenceKind = mn.getReferenceKind();
+ this.methodType = mn.getMethodOrFieldType();
+ byte refKind = mn.getReferenceKind();
+ if (refKind == REF_invokeSpecial && !mh.isInvokeSpecial())
+ // Devirtualized method invocation is usually formally virtual.
+ refKind = REF_invokeVirtual;
+ this.referenceKind = refKind;
}
public Class<?> getDeclaringClass() {
@@ -78,7 +82,6 @@
static String getReferenceKindString(int referenceKind) {
switch (referenceKind) {
- case REF_NONE: return "REF_NONE";
case REF_getField: return "getfield";
case REF_getStatic: return "getstatic";
case REF_putField: return "putfield";
diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java
index d2a499e..f4cad4d 100644
--- a/src/share/classes/java/lang/invoke/MethodHandles.java
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1237,7 +1237,7 @@
checkMethod(refKind, refc, method);
if (method.isMethodHandleInvoke())
return fakeMethodHandleInvoke(method);
- MethodHandle mh = DirectMethodHandle.make(refc, method);
+ MethodHandle mh = DirectMethodHandle.make(refKind, refc, method);
mh = maybeBindCaller(method, mh, callerClass);
mh = mh.setVarargs(method);
if (doRestrict)
diff --git a/src/share/classes/java/lang/invoke/SerializedLambda.java b/src/share/classes/java/lang/invoke/SerializedLambda.java
index 1a2db3f..3679e3f 100644
--- a/src/share/classes/java/lang/invoke/SerializedLambda.java
+++ b/src/share/classes/java/lang/invoke/SerializedLambda.java
@@ -40,7 +40,7 @@
*/
public final class SerializedLambda implements Serializable {
private static final long serialVersionUID = 8025925345765570181L;
- private final String capturingClass;
+ private final Class<?> capturingClass;
private final String functionalInterfaceClass;
private final String functionalInterfaceMethodName;
private final String functionalInterfaceMethodSignature;
@@ -73,7 +73,7 @@
* @param capturedArgs The dynamic arguments to the lambda factory site, which represent variables captured by
* the lambda
*/
- public SerializedLambda(String capturingClass,
+ public SerializedLambda(Class<?> capturingClass,
int functionalInterfaceMethodKind,
String functionalInterfaceClass,
String functionalInterfaceMethodName,
@@ -99,7 +99,7 @@
/** Get the name of the class that captured this lambda */
public String getCapturingClass() {
- return capturingClass;
+ return capturingClass.getName().replace('.', '/');
}
/** Get the name of the functional interface class to which this lambda has been converted */
@@ -166,9 +166,7 @@
Method deserialize = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
@Override
public Method run() throws Exception {
- Class<?> clazz = Class.forName(capturingClass.replace('/', '.'), true,
- Thread.currentThread().getContextClassLoader());
- Method m = clazz.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
+ Method m = capturingClass.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
m.setAccessible(true);
return m;
}
@@ -196,14 +194,4 @@
MethodHandleInfo.getReferenceKindString(implMethodKind), implClass, implMethodName,
implMethodSignature, instantiatedMethodType, capturedArgs.length);
}
-
- /*
- // @@@ Review question: is it worthwhile implementing a versioned serialization protocol?
-
- private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
- }
-
- private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
- }
-*/
}
diff --git a/test/java/lang/invoke/7087570/Test7087570.java b/test/java/lang/invoke/7087570/Test7087570.java
new file mode 100644
index 0000000..572facc
--- /dev/null
+++ b/test/java/lang/invoke/7087570/Test7087570.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/* @test
+ * @bug 7087570
+ * @summary REF_invokeSpecial DMHs (which are unusual) get marked explicitly; tweak the MHI to use this bit
+ *
+ * @run main Test7087570
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+
+public class Test7087570 {
+ // XXX may remove the following constant declarations when MethodHandleInfo is made public
+ private static final int
+ REF_getField = 1,
+ REF_getStatic = 2,
+ REF_putField = 3,
+ REF_putStatic = 4,
+ REF_invokeVirtual = 5,
+ REF_invokeStatic = 6,
+ REF_invokeSpecial = 7,
+ REF_newInvokeSpecial = 8,
+ REF_invokeInterface = 9,
+ REF_LIMIT = 10;
+
+ private static final TestMethodData[] TESTS = new TestMethodData[] {
+ // field accessors
+ data(DummyFieldHolder.class, "instanceField", getterMethodType(String.class), DummyFieldHolder.class, REF_getField),
+ data(DummyFieldHolder.class, "instanceField", setterMethodType(String.class), DummyFieldHolder.class, REF_putField),
+ data(DummyFieldHolder.class, "staticField", getterMethodType(Integer.class), DummyFieldHolder.class, REF_getStatic),
+ data(DummyFieldHolder.class, "staticField", setterMethodType(Integer.class), DummyFieldHolder.class, REF_putStatic),
+ data(DummyFieldHolder.class, "instanceByteField", getterMethodType(byte.class), DummyFieldHolder.class, REF_getField),
+ data(DummyFieldHolder.class, "instanceByteField", setterMethodType(byte.class), DummyFieldHolder.class, REF_putField),
+
+ // REF_invokeVirtual
+ data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeVirtual),
+
+ // REF_invokeVirtual strength-reduced to REF_invokeSpecial,
+ // test if it normalizes back to REF_invokeVirtual in MethodHandleInfo as expected
+ data(String.class, "hashCode", methodType(int.class), String.class, REF_invokeVirtual),
+
+ // REF_invokeStatic
+ data(Collections.class, "sort", methodType(void.class, List.class), Collections.class, REF_invokeStatic),
+ data(Arrays.class, "asList", methodType(List.class, Object[].class), Arrays.class, REF_invokeStatic), // varargs case
+
+ // REF_invokeSpecial
+ data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeSpecial),
+
+ // REF_newInvokeSpecial
+ data(String.class, "<init>", methodType(void.class, char[].class), String.class, REF_newInvokeSpecial),
+ data(DummyFieldHolder.class, "<init>", methodType(void.class, byte.class, Long[].class), DummyFieldHolder.class, REF_newInvokeSpecial), // varargs case
+
+ // REF_invokeInterface
+ data(List.class, "size", methodType(int.class), List.class, REF_invokeInterface)
+ };
+
+ public static void main(String... args) throws Throwable {
+ testWithLookup();
+ testWithUnreflect();
+ }
+
+ private static void doTest(MethodHandle mh, TestMethodData testMethod) {
+ Object mhi = newMethodHandleInfo(mh);
+
+ System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
+ testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
+ REF_KIND_NAMES[testMethod.referenceKind],
+ REF_KIND_NAMES[getReferenceKind(mhi)]);
+ assertEquals(testMethod.name, getName(mhi));
+ assertEquals(testMethod.methodType, getMethodType(mhi));
+ assertEquals(testMethod.declaringClass, getDeclaringClass(mhi));
+ assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
+ assertRefKindEquals(testMethod.referenceKind, getReferenceKind(mhi));
+ }
+
+ private static void testWithLookup() throws Throwable {
+ for (TestMethodData testMethod : TESTS) {
+ MethodHandle mh = lookupFrom(testMethod);
+ doTest(mh, testMethod);
+ }
+ }
+
+ private static void testWithUnreflect() throws Throwable {
+ for (TestMethodData testMethod : TESTS) {
+ MethodHandle mh = unreflectFrom(testMethod);
+ doTest(mh, testMethod);
+ }
+ }
+
+ private static MethodType getterMethodType(Class<?> clazz) {
+ return methodType(clazz);
+ }
+
+ private static MethodType setterMethodType(Class<?> clazz) {
+ return methodType(void.class, clazz);
+ }
+
+ private static final String[] REF_KIND_NAMES = {
+ "MH::invokeBasic",
+ "REF_getField", "REF_getStatic", "REF_putField", "REF_putStatic",
+ "REF_invokeVirtual", "REF_invokeStatic", "REF_invokeSpecial",
+ "REF_newInvokeSpecial", "REF_invokeInterface"
+ };
+
+ private static final Lookup LOOKUP = lookup();
+
+ // XXX may remove the following reflective logic when MethodHandleInfo is made public
+ private static final MethodHandle MH_IS_INVOKESPECIAL;
+ private static final MethodHandle MHI_CONSTRUCTOR;
+ private static final MethodHandle MHI_GET_NAME;
+ private static final MethodHandle MHI_GET_METHOD_TYPE;
+ private static final MethodHandle MHI_GET_DECLARING_CLASS;
+ private static final MethodHandle MHI_GET_REFERENCE_KIND;
+
+ static {
+ try {
+ // This is white box testing. Use reflection to grab private implementation bits.
+ String magicName = "IMPL_LOOKUP";
+ Field magicLookup = MethodHandles.Lookup.class.getDeclaredField(magicName);
+ // This unit test will fail if a security manager is installed.
+ magicLookup.setAccessible(true);
+ // Forbidden fruit...
+ Lookup directInvokeLookup = (Lookup) magicLookup.get(null);
+ Class<?> mhiClass = Class.forName("java.lang.invoke.MethodHandleInfo", false, MethodHandle.class.getClassLoader());
+ MH_IS_INVOKESPECIAL = directInvokeLookup
+ .findVirtual(MethodHandle.class, "isInvokeSpecial", methodType(boolean.class));
+ MHI_CONSTRUCTOR = directInvokeLookup
+ .findConstructor(mhiClass, methodType(void.class, MethodHandle.class));
+ MHI_GET_NAME = directInvokeLookup
+ .findVirtual(mhiClass, "getName", methodType(String.class));
+ MHI_GET_METHOD_TYPE = directInvokeLookup
+ .findVirtual(mhiClass, "getMethodType", methodType(MethodType.class));
+ MHI_GET_DECLARING_CLASS = directInvokeLookup
+ .findVirtual(mhiClass, "getDeclaringClass", methodType(Class.class));
+ MHI_GET_REFERENCE_KIND = directInvokeLookup
+ .findVirtual(mhiClass, "getReferenceKind", methodType(int.class));
+ } catch (ReflectiveOperationException ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static class TestMethodData {
+ final Class<?> clazz;
+ final String name;
+ final MethodType methodType;
+ final Class<?> declaringClass;
+ final int referenceKind; // the nominal refKind
+
+ public TestMethodData(Class<?> clazz, String name,
+ MethodType methodType, Class<?> declaringClass,
+ int referenceKind) {
+ this.clazz = clazz;
+ this.name = name;
+ this.methodType = methodType;
+ this.declaringClass = declaringClass;
+ this.referenceKind = referenceKind;
+ }
+ }
+
+ private static TestMethodData data(Class<?> clazz, String name,
+ MethodType methodType, Class<?> declaringClass,
+ int referenceKind) {
+ return new TestMethodData(clazz, name, methodType, declaringClass, referenceKind);
+ }
+
+ private static MethodHandle lookupFrom(TestMethodData testMethod)
+ throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
+ switch (testMethod.referenceKind) {
+ case REF_getField:
+ return LOOKUP.findGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
+ case REF_putField:
+ return LOOKUP.findSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
+ case REF_getStatic:
+ return LOOKUP.findStaticGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
+ case REF_putStatic:
+ return LOOKUP.findStaticSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
+ case REF_invokeVirtual:
+ case REF_invokeInterface:
+ return LOOKUP.findVirtual(testMethod.clazz, testMethod.name, testMethod.methodType);
+ case REF_invokeStatic:
+ return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
+ case REF_invokeSpecial:
+ Class<?> thisClass = LOOKUP.lookupClass();
+ return LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
+ case REF_newInvokeSpecial:
+ return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
+ default:
+ throw new Error("ERROR: unexpected referenceKind in test data");
+ }
+ }
+
+ private static MethodHandle unreflectFrom(TestMethodData testMethod)
+ throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
+ switch (testMethod.referenceKind) {
+ case REF_getField:
+ case REF_getStatic: {
+ Field f = testMethod.clazz.getDeclaredField(testMethod.name);
+ return LOOKUP.unreflectGetter(f);
+ }
+ case REF_putField:
+ case REF_putStatic: {
+ Field f = testMethod.clazz.getDeclaredField(testMethod.name);
+ return LOOKUP.unreflectSetter(f);
+ }
+ case REF_invokeVirtual:
+ case REF_invokeStatic:
+ case REF_invokeInterface: {
+ Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
+ return LOOKUP.unreflect(m);
+ }
+ case REF_invokeSpecial: {
+ Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
+ Class<?> thisClass = LOOKUP.lookupClass();
+ return LOOKUP.unreflectSpecial(m, thisClass);
+ }
+ case REF_newInvokeSpecial: {
+ Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
+ return LOOKUP.unreflectConstructor(c);
+ }
+ default:
+ throw new Error("ERROR: unexpected referenceKind in test data");
+ }
+ }
+
+ private static Object newMethodHandleInfo(MethodHandle mh) {
+ try {
+ return MHI_CONSTRUCTOR.invoke(mh);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static boolean isInvokeSpecial(MethodHandle mh) {
+ try {
+ return (boolean) MH_IS_INVOKESPECIAL.invokeExact(mh);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static String getName(Object mhi) {
+ try {
+ return (String) MHI_GET_NAME.invoke(mhi);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static MethodType getMethodType(Object mhi) {
+ try {
+ return (MethodType) MHI_GET_METHOD_TYPE.invoke(mhi);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static Class<?> getDeclaringClass(Object mhi) {
+ try {
+ return (Class<?>) MHI_GET_DECLARING_CLASS.invoke(mhi);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static int getReferenceKind(Object mhi) {
+ try {
+ return (int) MHI_GET_REFERENCE_KIND.invoke(mhi);
+ } catch (Throwable ex) {
+ throw new Error(ex);
+ }
+ }
+
+ private static void assertRefKindEquals(int expect, int observed) {
+ if (expect == observed) return;
+
+ String msg = "expected " + REF_KIND_NAMES[(int) expect] +
+ " but observed " + REF_KIND_NAMES[(int) observed];
+ System.out.println("FAILED: " + msg);
+ throw new AssertionError(msg);
+ }
+
+ private static void assertEquals(Object expect, Object observed) {
+ if (java.util.Objects.equals(expect, observed)) return;
+
+ String msg = "expected " + expect + " but observed " + observed;
+ System.out.println("FAILED: " + msg);
+ throw new AssertionError(msg);
+ }
+}
+
+class DummyFieldHolder {
+ public static Integer staticField;
+ public String instanceField;
+ public byte instanceByteField;
+
+ public DummyFieldHolder(byte unused1, Long... unused2) {
+ }
+}
+
diff --git a/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java b/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java
new file mode 100644
index 0000000..9d38102
--- /dev/null
+++ b/test/java/lang/invoke/lambda/LambdaClassLoaderSerialization.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+@test
+@bug 8004970
+@summary Lambda serialization in the presence of class loaders
+@author Peter Levart
+*/
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class LambdaClassLoaderSerialization {
+
+ public interface SerializableRunnable extends Runnable, Serializable {}
+
+ public static class MyCode implements SerializableRunnable {
+
+ private byte[] serialize(Object o) {
+ ByteArrayOutputStream baos;
+ try (
+ ObjectOutputStream oos =
+ new ObjectOutputStream(baos = new ByteArrayOutputStream())
+ ) {
+ oos.writeObject(o);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return baos.toByteArray();
+ }
+
+ private <T> T deserialize(byte[] bytes) {
+ try (
+ ObjectInputStream ois =
+ new ObjectInputStream(new ByteArrayInputStream(bytes))
+ ) {
+ return (T) ois.readObject();
+ }
+ catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void run() {
+ System.out.println(" this: " + this);
+
+ SerializableRunnable deSerializedThis = deserialize(serialize(this));
+ System.out.println(" deSerializedThis: " + deSerializedThis);
+
+ SerializableRunnable runnable = () -> {System.out.println("HELLO");};
+ System.out.println(" runnable: " + runnable);
+
+ SerializableRunnable deSerializedRunnable = deserialize(serialize(runnable));
+ System.out.println("deSerializedRunnable: " + deSerializedRunnable);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClassLoader myCl = new MyClassLoader(
+ LambdaClassLoaderSerialization.class.getClassLoader()
+ );
+ Class<?> myCodeClass = Class.forName(
+ LambdaClassLoaderSerialization.class.getName() + "$MyCode",
+ true,
+ myCl
+ );
+ Runnable myCode = (Runnable) myCodeClass.newInstance();
+ myCode.run();
+ }
+
+ static class MyClassLoader extends ClassLoader {
+ MyClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (name.indexOf('.') < 0) {
+ synchronized (getClassLoadingLock(name)) {
+ Class<?> c = findLoadedClass(name);
+ if (c == null) {
+ c = findClass(name);
+ }
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+ } else {
+ return super.loadClass(name, resolve);
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ String path = name.replace('.', '/').concat(".class");
+ try (InputStream is = getResourceAsStream(path)) {
+ if (is != null) {
+ byte[] bytes = readFully(is);
+ return defineClass(name, bytes, 0, bytes.length);
+ } else {
+ throw new ClassNotFoundException(name);
+ }
+ }
+ catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ }
+ }
+
+ static byte[] readFully(InputStream is) throws IOException {
+ byte[] output = {};
+ int pos = 0;
+ while (true) {
+ int bytesToRead;
+ if (pos >= output.length) { // Only expand when there's no room
+ bytesToRead = output.length + 1024;
+ if (output.length < pos + bytesToRead) {
+ output = Arrays.copyOf(output, pos + bytesToRead);
+ }
+ } else {
+ bytesToRead = output.length - pos;
+ }
+ int cc = is.read(output, pos, bytesToRead);
+ if (cc < 0) {
+ if (output.length != pos) {
+ output = Arrays.copyOf(output, pos);
+ }
+ break;
+ }
+ pos += cc;
+ }
+ return output;
+ }
+ }
+}
diff --git a/test/java/lang/invoke/lambda/LambdaSerialization.java b/test/java/lang/invoke/lambda/LambdaSerialization.java
index ebe846b..6d681c3 100644
--- a/test/java/lang/invoke/lambda/LambdaSerialization.java
+++ b/test/java/lang/invoke/lambda/LambdaSerialization.java
@@ -25,7 +25,6 @@
@test
@bug 8004970
@summary Lambda serialization
-@run main/othervm LambdaSerialization
*/
import java.io.*;