8200261: Regression with JVM anonymous class
Summary: Restore resolved anonymous class when creating a new constantpool because of overpass methods
Reviewed-by: coleenp, lfoltan
--- a/src/hotspot/share/classfile/classFileParser.cpp Wed Apr 04 18:19:46 2018 -0400
+++ b/src/hotspot/share/classfile/classFileParser.cpp Thu Apr 05 13:19:25 2018 -0400
@@ -5424,6 +5424,8 @@
// has to be changed accordingly.
ik->set_initial_method_idnum(ik->methods()->length());
+ ik->set_this_class_index(_this_class_index);
+
if (is_anonymous()) {
// _this_class_index is a CONSTANT_Class entry that refers to this
// anonymous class itself. If this class needs to refer to its own methods or
--- a/src/hotspot/share/classfile/defaultMethods.cpp Wed Apr 04 18:19:46 2018 -0400
+++ b/src/hotspot/share/classfile/defaultMethods.cpp Thu Apr 05 13:19:25 2018 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2018, 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
@@ -884,6 +884,10 @@
if (new_methods->length() > 0) {
ConstantPool* cp = bpool->create_constant_pool(CHECK);
if (cp != klass->constants()) {
+ // Copy resolved anonymous class into new constant pool.
+ if (klass->is_anonymous()) {
+ cp->klass_at_put(klass->this_class_index(), klass);
+ }
klass->class_loader_data()->add_to_deallocate_list(klass->constants());
klass->set_constants(cp);
cp->set_pool_holder(klass);
--- a/src/hotspot/share/oops/instanceKlass.hpp Wed Apr 04 18:19:46 2018 -0400
+++ b/src/hotspot/share/oops/instanceKlass.hpp Thu Apr 05 13:19:25 2018 -0400
@@ -250,6 +250,7 @@
u1 _init_state; // state of class
u1 _reference_type; // reference type
+ u2 _this_class_index; // constant pool entry
#if INCLUDE_JVMTI
JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration
#endif
@@ -516,6 +517,10 @@
_reference_type = (u1)t;
}
+ // this class cp index
+ u2 this_class_index() const { return _this_class_index; }
+ void set_this_class_index(u2 index) { _this_class_index = index; }
+
static ByteSize reference_type_offset() { return in_ByteSize(offset_of(InstanceKlass, _reference_type)); }
// find local field, returns true if found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/defineAnonClass/UnsafeDefMeths.java Thu Apr 05 13:19:25 2018 -0400
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2018, 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 8200261
+ * @summary Tests an anonymous class that implements interfaces with default methods.
+ * @library /testlibrary
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ * java.management
+ * @compile -XDignore.symbol.file=true UnsafeDefMeths.java
+ * @run main UnsafeDefMeths
+ */
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+import sun.misc.Unsafe;
+
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
+import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
+
+public class UnsafeDefMeths {
+
+ static final Unsafe UNSAFE;
+
+ static {
+ try {
+ Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
+ unsafeField.setAccessible(true);
+ UNSAFE = (Unsafe) unsafeField.get(null);
+ }
+ catch (Exception e) {
+ throw new InternalError(e);
+ }
+ }
+
+ interface Resource {
+ Pointer ptr();
+ }
+
+ interface Struct extends Resource {
+ StructPointer ptr();
+ }
+
+ interface Pointer { }
+
+ interface StructPointer extends Pointer { }
+
+ interface I extends Struct {
+ void m();
+ }
+
+ static String IMPL_PREFIX = "$$impl";
+ static String PTR_FIELD_NAME = "ptr";
+
+ public static void main(String[] args) throws Throwable {
+ byte[] bytes = new UnsafeDefMeths().generate(I.class);
+ Class<?> cl = UNSAFE.defineAnonymousClass(I.class, bytes, new Object[0]);
+ I i = (I)cl.getConstructors()[0].newInstance(new Object[] { null }); //exception here!
+ }
+
+ // Generate a class similar to:
+ //
+ // public class UnsafeDefMeths$I$$impl implements UnsafeDefMeths$I, UnsafeDefMeths$Struct {
+ //
+ // public UnsafeDefMeths$StructPointer ptr;
+ //
+ // public UnsafeDefMeths$I$$impl(UnsafeDefMeths$StructPointer p) {
+ // ptr = p;
+ // }
+ //
+ // public UnsafeDefMeths$StructPointer ptr() {
+ // return ptr;
+ // }
+ // }
+ //
+ byte[] generate(Class<?> iface) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+ String ifaceTypeName = Type.getInternalName(iface);
+ String proxyClassName = ifaceTypeName + IMPL_PREFIX;
+ // class definition
+ cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, proxyClassName,
+ desc(Object.class) + desc(ifaceTypeName) + desc(Struct.class),
+ name(Object.class),
+ new String[] { ifaceTypeName, name(Struct.class) });
+
+ cw.visitField(ACC_PUBLIC, PTR_FIELD_NAME, desc(StructPointer.class), desc(StructPointer.class), null);
+ cw.visitEnd();
+
+ // constructor
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>",
+ meth(desc(void.class), desc(StructPointer.class)),
+ meth(desc(void.class), desc(StructPointer.class)), null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, name(Object.class), "<init>", meth(desc(void.class)), false);
+ mv.visitVarInsn(ALOAD, 1);
+ // Execution of this PUTFIELD instruction causes the bug's ClassNotFoundException.
+ mv.visitFieldInsn(PUTFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // ptr() impl
+ mv = cw.visitMethod(ACC_PUBLIC, PTR_FIELD_NAME, meth(desc(StructPointer.class)),
+ meth(desc(StructPointer.class)), null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ String name(Class<?> clazz) {
+ if (clazz.isPrimitive()) {
+ throw new IllegalStateException();
+ } else if (clazz.isArray()) {
+ return desc(clazz);
+ } else {
+ return clazz.getName().replaceAll("\\.", "/");
+ }
+ }
+
+ String desc(Class<?> clazz) {
+ String mdesc = MethodType.methodType(clazz).toMethodDescriptorString();
+ return mdesc.substring(mdesc.indexOf(')') + 1);
+ }
+
+ String desc(String clazzName) {
+ return "L" + clazzName + ";";
+ }
+
+ String gen(String clazz, String... typeargs) {
+ return clazz.substring(0, clazz.length() - 1) + Stream.of(typeargs).collect(Collectors.joining("", "<", ">")) + ";";
+ }
+
+ String meth(String restype, String... argtypes) {
+ return Stream.of(argtypes).collect(Collectors.joining("", "(", ")")) + restype;
+ }
+
+ String meth(Method m) {
+ return MethodType.methodType(m.getReturnType(), m.getParameterTypes()).toMethodDescriptorString();
+ }
+}