8200261: Regression with JVM anonymous class

Restore resolved anonymous class when creating a new constantpool because of overpass methods

Reviewed-by: coleenp, lfoltan
This commit is contained in:
Harold Seigel 2018-04-05 13:19:25 -04:00
parent 094178fdba
commit b89ae10df2
4 changed files with 197 additions and 1 deletions

View file

@ -5424,6 +5424,8 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
// has to be changed accordingly. // has to be changed accordingly.
ik->set_initial_method_idnum(ik->methods()->length()); ik->set_initial_method_idnum(ik->methods()->length());
ik->set_this_class_index(_this_class_index);
if (is_anonymous()) { if (is_anonymous()) {
// _this_class_index is a CONSTANT_Class entry that refers to this // _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 // anonymous class itself. If this class needs to refer to its own methods or

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -884,6 +884,10 @@ static void switchover_constant_pool(BytecodeConstantPool* bpool,
if (new_methods->length() > 0) { if (new_methods->length() > 0) {
ConstantPool* cp = bpool->create_constant_pool(CHECK); ConstantPool* cp = bpool->create_constant_pool(CHECK);
if (cp != klass->constants()) { 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->class_loader_data()->add_to_deallocate_list(klass->constants());
klass->set_constants(cp); klass->set_constants(cp);
cp->set_pool_holder(klass); cp->set_pool_holder(klass);

View file

@ -250,6 +250,7 @@ class InstanceKlass: public Klass {
u1 _init_state; // state of class u1 _init_state; // state of class
u1 _reference_type; // reference type u1 _reference_type; // reference type
u2 _this_class_index; // constant pool entry
#if INCLUDE_JVMTI #if INCLUDE_JVMTI
JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration
#endif #endif
@ -516,6 +517,10 @@ class InstanceKlass: public Klass {
_reference_type = (u1)t; _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)); } static ByteSize reference_type_offset() { return in_ByteSize(offset_of(InstanceKlass, _reference_type)); }
// find local field, returns true if found // find local field, returns true if found

View file

@ -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();
}
}