changeset 49541:a1a7456dd8b9

8200261: Regression with JVM anonymous class Summary: Restore resolved anonymous class when creating a new constantpool because of overpass methods Reviewed-by: coleenp, lfoltan
author hseigel
date Thu, 05 Apr 2018 13:19:25 -0400
parents 0bb0c2f27ca9
children fa26e7c6efb7
files src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/classfile/defaultMethods.cpp src/hotspot/share/oops/instanceKlass.hpp test/hotspot/jtreg/runtime/defineAnonClass/UnsafeDefMeths.java
diffstat 4 files changed, 197 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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();
+    }
+}