--- old/src/share/vm/classfile/classFileParser.cpp 2016-09-02 08:37:23.323764330 -0400 +++ new/src/share/vm/classfile/classFileParser.cpp 2016-09-02 08:37:22.034738193 -0400 @@ -5402,6 +5402,59 @@ debug_only(ik->verify();) } +// For an anonymous class that is in the unnamed package, move it to its host class's +// package by prepending its host class's package name to its class name and setting +// its _class_name field. +void ClassFileParser::prepend_host_package_name(const InstanceKlass* host_klass, TRAPS) { + ResourceMark rm(THREAD); + assert(strrchr(_class_name->as_C_string(), '/') == NULL, + "Anonymous class should not be in a package"); + const char* host_pkg_name = + ClassLoader::package_from_name(host_klass->name()->as_C_string(), NULL); + + if (host_pkg_name != NULL) { + size_t host_pkg_len = strlen(host_pkg_name); + int class_name_len = _class_name->utf8_length(); + char* new_anon_name = + NEW_RESOURCE_ARRAY(char, host_pkg_len + 1 + class_name_len); + // Copy host package name and trailing /. + strncpy(new_anon_name, host_pkg_name, host_pkg_len); + new_anon_name[host_pkg_len] = '/'; + // Append anonymous class name. The anonymous class name can contain odd + // characters. So, do a strncpy instead of using sprintf("%s..."). + strncpy(new_anon_name + host_pkg_len + 1, (char *)_class_name->base(), class_name_len); + + // Create a symbol and update the anonymous class name. + _class_name = SymbolTable::lookup(new_anon_name, + (int)host_pkg_len + 1 + class_name_len, + CHECK); + } +} + +// If the host class and the anonymous class are in the same package then do +// nothing. If the anonymous class is in the unnamed package then move it to its +// host's package. If the classes are in different packages then throw an IAE +// exception. +void ClassFileParser::fix_anonymous_class_name(TRAPS) { + assert(_host_klass != NULL, "Expected an anonymous class"); + + const jbyte* anon_last_slash = UTF8::strrchr(_class_name->base(), + _class_name->utf8_length(), '/'); + if (anon_last_slash == NULL) { // Unnamed package + prepend_host_package_name(_host_klass, CHECK); + } else { + if (!InstanceKlass::is_same_class_package(_host_klass->class_loader(), + _host_klass->name(), + _host_klass->class_loader(), + _class_name)) { + ResourceMark rm(THREAD); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("Host class %s and anonymous class %s are in different packages", + _host_klass->name()->as_C_string(), _class_name->as_C_string())); + } + } +} + static bool relax_format_check_for(ClassLoaderData* loader_data) { bool trusted = (loader_data->is_the_null_class_loader_data() || SystemDictionary::is_platform_class_loader(loader_data->class_loader())); @@ -5417,7 +5470,7 @@ Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, Publicity pub_level, TRAPS) : @@ -5692,6 +5745,13 @@ return; } + // if this is an anonymous class fix up its name if it's in the unnamed + // package. Otherwise, throw IAE if it is in a different package than + // its host class. + if (_host_klass != NULL) { + fix_anonymous_class_name(CHECK); + } + // Verification prevents us from creating names with dots in them, this // asserts that that's the case. assert(is_internal_format(_class_name), "external class name format used internally"); --- old/src/share/vm/classfile/classFileParser.hpp 2016-09-02 08:37:37.302323454 -0400 +++ new/src/share/vm/classfile/classFileParser.hpp 2016-09-02 08:37:36.283841080 -0400 @@ -79,7 +79,7 @@ const Symbol* _requested_name; Symbol* _class_name; mutable ClassLoaderData* _loader_data; - const Klass* _host_klass; + const InstanceKlass* _host_klass; GrowableArray* _cp_patches; // overrides for CP entries // Metadata created before the instance klass is created. Must be deallocated @@ -155,6 +155,9 @@ ConstantPool* cp, TRAPS); + void prepend_host_package_name(const InstanceKlass* host_klass, TRAPS); + void fix_anonymous_class_name(TRAPS); + void fill_instance_klass(InstanceKlass* ik, bool cf_changed_in_CFLH, TRAPS); void set_klass(InstanceKlass* instance); @@ -474,7 +477,7 @@ Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, Publicity pub_level, TRAPS); @@ -500,7 +503,7 @@ bool is_anonymous() const { return _host_klass != NULL; } bool is_interface() const { return _access_flags.is_interface(); } - const Klass* host_klass() const { return _host_klass; } + const InstanceKlass* host_klass() const { return _host_klass; } const GrowableArray* cp_patches() const { return _cp_patches; } ClassLoaderData* loader_data() const { return _loader_data; } const Symbol* class_name() const { return _class_name; } --- old/src/share/vm/classfile/klassFactory.cpp 2016-09-02 08:37:51.631304663 -0400 +++ new/src/share/vm/classfile/klassFactory.cpp 2016-09-02 08:37:49.995077188 -0400 @@ -94,7 +94,7 @@ Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, TRAPS) { --- old/src/share/vm/classfile/klassFactory.hpp 2016-09-02 08:38:05.795259281 -0400 +++ new/src/share/vm/classfile/klassFactory.hpp 2016-09-02 08:38:04.774240122 -0400 @@ -72,7 +72,7 @@ Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, TRAPS); }; --- old/src/share/vm/classfile/systemDictionary.cpp 2016-09-02 08:38:19.389170723 -0400 +++ new/src/share/vm/classfile/systemDictionary.cpp 2016-09-02 08:38:18.431856437 -0400 @@ -1027,7 +1027,7 @@ Handle class_loader, Handle protection_domain, ClassFileStream* st, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, TRAPS) { --- old/src/share/vm/classfile/systemDictionary.hpp 2016-09-02 08:38:33.270721151 -0400 +++ new/src/share/vm/classfile/systemDictionary.hpp 2016-09-02 08:38:32.302802004 -0400 @@ -299,7 +299,7 @@ Handle class_loader, Handle protection_domain, ClassFileStream* st, - const Klass* host_klass, + const InstanceKlass* host_klass, GrowableArray* cp_patches, TRAPS); --- old/src/share/vm/classfile/verifier.cpp 2016-09-02 08:38:46.294218209 -0400 +++ new/src/share/vm/classfile/verifier.cpp 2016-09-02 08:38:45.347693088 -0400 @@ -2786,7 +2786,7 @@ // direct interface relative to the host class have_imr_indirect = (have_imr_indirect && !is_same_or_direct_interface( - InstanceKlass::cast(current_class()->host_klass()), + current_class()->host_klass(), host_klass_type, ref_class_type)); } if (!subtype) { --- old/src/share/vm/oops/instanceKlass.hpp 2016-09-02 08:39:01.827272640 -0400 +++ new/src/share/vm/oops/instanceKlass.hpp 2016-09-02 08:39:00.726920468 -0400 @@ -619,8 +619,8 @@ objArrayOop signers() const; // host class - Klass* host_klass() const { - Klass** hk = (Klass**)adr_host_klass(); + InstanceKlass* host_klass() const { + InstanceKlass** hk = adr_host_klass(); if (hk == NULL) { return NULL; } else { @@ -628,9 +628,9 @@ return *hk; } } - void set_host_klass(const Klass* host) { + void set_host_klass(const InstanceKlass* host) { assert(is_anonymous(), "not anonymous"); - const Klass** addr = (const Klass**)adr_host_klass(); + const InstanceKlass** addr = (const InstanceKlass **)adr_host_klass(); assert(addr != NULL, "no reversed space"); if (addr != NULL) { *addr = host; @@ -1057,13 +1057,13 @@ } }; - Klass** adr_host_klass() const { + InstanceKlass** adr_host_klass() const { if (is_anonymous()) { - Klass** adr_impl = adr_implementor(); + InstanceKlass** adr_impl = (InstanceKlass **)adr_implementor(); if (adr_impl != NULL) { return adr_impl + 1; } else { - return end_of_nonstatic_oop_maps(); + return (InstanceKlass **)end_of_nonstatic_oop_maps(); } } else { return NULL; --- old/src/share/vm/prims/unsafe.cpp 2016-09-02 08:39:15.307080987 -0400 +++ new/src/share/vm/prims/unsafe.cpp 2016-09-02 08:39:14.127152678 -0400 @@ -779,6 +779,7 @@ // define a class but do not make it known to the class loader or system dictionary // - host_class: supplies context for linkage, access control, protection domain, and class loader +// if host_class is itself anonymous then it is replaced with its host class. // - data: bytes of a class file, a raw memory address (length gives the number of bytes) // - cp_patches: where non-null entries exist, they replace corresponding CP entries in data @@ -787,8 +788,12 @@ // link to any member of U. Just after U is loaded, the only way to use it is reflectively, // through java.lang.Class methods like Class.newInstance. +// The package of an anonymous class must either match its host's class's package or be in the +// unnamed package. If it is in the unnamed package then it will be put in its host class's +// package. +// + // Access checks for linkage sites within U continue to follow the same rules as for named classes. -// The package of an anonymous class is given by the package qualifier on the name under which it was loaded. // An anonymous class also has special privileges to access any member of its host class. // This is the main reason why this loading operation is unsafe. The purpose of this is to // allow language implementations to simulate "open classes"; a host class in effect gets @@ -870,9 +875,11 @@ // Primitive types have NULL Klass* fields in their java.lang.Class instances. if (host_klass == NULL) { - THROW_0(vmSymbols::java_lang_IllegalArgumentException()); + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Host class is null"); } + assert(host_klass->is_instance_klass(), "Host class must be an instance class"); + const char* host_source = host_klass->external_name(); Handle host_loader(THREAD, host_klass->class_loader()); Handle host_domain(THREAD, host_klass->protection_domain()); @@ -903,7 +910,7 @@ host_loader, host_domain, &st, - host_klass, + InstanceKlass::cast(host_klass), cp_patches, CHECK_NULL); if (anonk == NULL) { --- old/src/share/vm/runtime/reflection.cpp 2016-09-02 08:39:29.004650513 -0400 +++ new/src/share/vm/runtime/reflection.cpp 2016-09-02 08:39:27.897730178 -0400 @@ -412,13 +412,13 @@ return result; } -static bool under_host_klass(const InstanceKlass* ik, const Klass* host_klass) { +static bool under_host_klass(const InstanceKlass* ik, const InstanceKlass* host_klass) { DEBUG_ONLY(int inf_loop_check = 1000 * 1000 * 1000); for (;;) { - const Klass* hc = (const Klass*)ik->host_klass(); + const InstanceKlass* hc = ik->host_klass(); if (hc == NULL) return false; if (hc == host_klass) return true; - ik = InstanceKlass::cast(hc); + ik = hc; // There's no way to make a host class loop short of patching memory. // Therefore there cannot be a loop here unless there's another bug. @@ -436,8 +436,8 @@ // If either is on the other's host_klass chain, access is OK, // because one is inside the other. - if (under_host_klass(accessor_ik, accessee) || - under_host_klass(accessee_ik, accessor)) + if (under_host_klass(accessor_ik, accessee_ik) || + under_host_klass(accessee_ik, accessor_ik)) return true; if ((RelaxAccessControlCheck && --- old/test/compiler/jsr292/CallSiteDepContextTest.java 2016-09-02 08:39:42.978534177 -0400 +++ new/test/compiler/jsr292/CallSiteDepContextTest.java 2016-09-02 08:39:41.884788201 -0400 @@ -62,7 +62,7 @@ public class CallSiteDepContextTest { static final Unsafe UNSAFE = Unsafe.getUnsafe(); static final MethodHandles.Lookup LOOKUP = MethodHandleHelper.IMPL_LOOKUP; - static final String CLASS_NAME = "java/lang/invoke/Test"; + static final String CLASS_NAME = "compiler/jsr292/Test"; static final String METHOD_NAME = "m"; static final MethodType TYPE = MethodType.methodType(int.class); @@ -129,8 +129,8 @@ } public static void testSharedCallSite() throws Throwable { - Class cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null); - Class cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null); + Class cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_1"), null); + Class cls2 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("CS_2"), null); MethodHandle[] mhs = new MethodHandle[] { LOOKUP.findStatic(cls1, METHOD_NAME, TYPE), @@ -151,7 +151,7 @@ execute(1, mh); // mcs.context == cls1 - Class cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null); + Class cls1 = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("NonBound_1"), null); MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE); execute(1, mh1); @@ -170,8 +170,8 @@ mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE)); Class[] cls = new Class[] { - UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1" + id), null), - UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2" + id), null), + UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_1" + id), null), + UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_2" + id), null), }; MethodHandle[] mhs = new MethodHandle[] { @@ -185,7 +185,7 @@ execute(1, mhs); ref = new PhantomReference<>(cls[0], rq); - cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3" + id), null); + cls[0] = UNSAFE.defineAnonymousClass(CallSiteDepContextTest.class, getClassFile("GC_3" + id), null); mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE); do { --- /dev/null 2016-09-01 07:57:02.195527988 -0400 +++ new/test/runtime/defineAnonClass/DefineAnon.java 2016-09-02 08:39:55.617884330 -0400 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016, 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 DefineAnon + * @bug 8058575 + * @library /testlibrary + * @modules java.base/jdk.internal.org.objectweb.asm + * java.management + * @compile -XDignore.symbol.file=true DefineAnon.java + * @run main/othervm p1.DefineAnon + */ + +package p1; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import sun.misc.Unsafe; + + +class T { + static protected void test0() { System.out.println("test0 (public)"); } + static protected void test1() { System.out.println("test1 (protected)"); } + static /*package-private*/ void test2() { System.out.println("test2 (package)"); } + static private void test3() { System.out.println("test3 (private)"); } +} + +public class DefineAnon { + + private static Unsafe getUnsafe() { + try { + java.lang.reflect.Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); + singleoneInstanceField.setAccessible(true); + return (Unsafe) singleoneInstanceField.get(null); + } catch (Throwable ex) { + throw new RuntimeException("Was unable to get Unsafe instance."); + } + } + + static Unsafe UNSAFE = DefineAnon.getUnsafe(); + + static Class getAnonClass(Class hostClass, final String className) { + final String superName = "java/lang/Object"; + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, className, null, superName, null); + + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC, "test", "()V", null, null); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test0", "()V", false); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test1", "()V", false); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test2", "()V", false); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "p1/T", "test3", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + final byte[] classBytes = cw.toByteArray(); + Class invokerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, new Object[0]); + UNSAFE.ensureClassInitialized(invokerClass); + return invokerClass; + } + + public static void main(String[] args) throws Throwable { + Throwable fail = null; + + // Anonymous class has the privileges of its host class, so test[0123] should all work. + System.out.println("Injecting from the same package (p1):"); + Class p1cls = getAnonClass(T.class, "p1/AnonClass"); + try { + p1cls.getMethod("test").invoke(null); + } catch (Throwable ex) { + ex.printStackTrace(); + fail = ex; // throw this to make test fail, since subtest failed + } + + // Anonymous class has different package name from host class. Should throw + // IllegalArgumentException. + System.out.println("Injecting from the wrong package (p2):"); + try { + Class p2cls = getAnonClass(DefineAnon.class, "p2/AnonClass"); + p2cls.getMethod("test").invoke(null); + System.out.println("Failed, did not get expected IllegalArgumentException"); + } catch (java.lang.IllegalArgumentException e) { + if (e.getMessage().contains("Host class p1/DefineAnon and anonymous class p2/AnonClass")) { + System.out.println("Got expected IllegalArgumentException: " + e.getMessage()); + } else { + throw new RuntimeException("Unexpected message: " + e.getMessage()); + } + } catch (Throwable ex) { + ex.printStackTrace(); + fail = ex; // throw this to make test fail, since subtest failed + } + + // Inject a class in the unnamed package into p1.T. It should be able + // to access all methods in p1.T. + System.out.println("Injecting unnamed package into correct host class:"); + try { + Class p3cls = getAnonClass(T.class, "AnonClass"); + p3cls.getMethod("test").invoke(null); + } catch (Throwable ex) { + ex.printStackTrace(); + fail = ex; // throw this to make test fail, since subtest failed + } + + // Try using an array class as the host class. This should throw IllegalArgumentException. + try { + Class p3cls = getAnonClass(String[].class, "AnonClass"); + throw new RuntimeException("Expected IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ex) { + } + + if (fail != null) throw fail; // make test fail, since subtest failed + } +} --- /dev/null 2016-09-01 07:57:02.195527988 -0400 +++ new/test/runtime/defineAnonClass/NestedUnsafe.java 2016-09-02 08:40:02.295216941 -0400 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, 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 8058575 + * @summary Creates an anonymous class inside of an anonymous class. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * @run main p.NestedUnsafe + */ + +package p; + +import java.security.ProtectionDomain; +import java.io.InputStream; +import java.lang.*; +import jdk.test.lib.*; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.unsafe.UnsafeHelper; + + +// Test that an anonymous class in package 'p' cannot define its own anonymous class +// in another package. +public class NestedUnsafe { + // The String concatenation should create the nested anonymous class. + static byte klassbuf[] = InMemoryJavaCompiler.compile("q.TestClass", + "package q; " + + "public class TestClass { " + + " public static void concat(String one, String two) throws Throwable { " + + " System.out.println(one + two);" + + " } } "); + + public static void main(String args[]) throws Exception { + Unsafe unsafe = UnsafeHelper.getUnsafe(); + + // The anonymous class calls defineAnonymousClass creating a nested anonymous class. + byte klassbuf2[] = InMemoryJavaCompiler.compile("p.TestClass2", + "package p; " + + "import jdk.internal.misc.Unsafe; " + + "public class TestClass2 { " + + " public static void doit() throws Throwable { " + + " Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); " + + " Class klass2 = unsafe.defineAnonymousClass(TestClass2.class, p.NestedUnsafe.klassbuf, new Object[0]); " + + " unsafe.ensureClassInitialized(klass2); " + + " Class[] dArgs = new Class[2]; " + + " dArgs[0] = String.class; " + + " dArgs[1] = String.class; " + + " try { " + + " klass2.getMethod(\"concat\", dArgs).invoke(null, \"CC\", \"DD\"); " + + " } catch (Throwable ex) { " + + " throw new RuntimeException(\"Exception: \" + ex.toString()); " + + " } " + + "} } ", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED"); + + Class klass2 = unsafe.defineAnonymousClass(p.NestedUnsafe.class, klassbuf2, new Object[0]); + try { + klass2.getMethod("doit").invoke(null); + throw new RuntimeException("Expected exception not thrown"); + } catch (Throwable ex) { + Throwable iae = ex.getCause(); + if (!iae.toString().contains( + "IllegalArgumentException: Host class p/NestedUnsafe and anonymous class q/TestClass")) { + throw new RuntimeException("Exception: " + iae.toString()); + } + } + } +} --- /dev/null 2016-09-01 07:57:02.195527988 -0400 +++ new/test/runtime/defineAnonClass/NestedUnsafe2.java 2016-09-02 08:40:08.374634697 -0400 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016, 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 8058575 + * @summary Creates an anonymous class inside of an anonymous class. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.compiler + * java.management + * @run main p.NestedUnsafe2 + */ + +package p; + +import java.security.ProtectionDomain; +import java.io.InputStream; +import java.lang.*; +import jdk.test.lib.*; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.unsafe.UnsafeHelper; + + +// Test that an anonymous class that gets put in its host's package cannot define +// an anonymous class in another package. +public class NestedUnsafe2 { + // The String concatenation should create the nested anonymous class. + public static byte klassbuf[] = InMemoryJavaCompiler.compile("q.TestClass", + "package q; " + + "public class TestClass { " + + " public static void concat(String one, String two) throws Throwable { " + + " System.out.println(one + two);" + + " } } "); + + public static void main(String args[]) throws Exception { + Unsafe unsafe = UnsafeHelper.getUnsafe(); + + // The anonymous class calls defineAnonymousClass creating a nested anonymous class. + byte klassbuf2[] = InMemoryJavaCompiler.compile("TestClass2", + "import jdk.internal.misc.Unsafe; " + + "public class TestClass2 { " + + " public static void doit() throws Throwable { " + + " Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); " + + " Class klass2 = unsafe.defineAnonymousClass(TestClass2.class, p.NestedUnsafe2.klassbuf, new Object[0]); " + + " unsafe.ensureClassInitialized(klass2); " + + " Class[] dArgs = new Class[2]; " + + " dArgs[0] = String.class; " + + " dArgs[1] = String.class; " + + " try { " + + " klass2.getMethod(\"concat\", dArgs).invoke(null, \"CC\", \"DD\"); " + + " } catch (Throwable ex) { " + + " throw new RuntimeException(\"Exception: \" + ex.toString()); " + + " } " + + "} } ", + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED"); + + Class klass2 = unsafe.defineAnonymousClass(p.NestedUnsafe2.class, klassbuf2, new Object[0]); + try { + klass2.getMethod("doit").invoke(null); + throw new RuntimeException("Expected exception not thrown"); + } catch (Throwable ex) { + Throwable iae = ex.getCause(); + if (!iae.toString().contains( + "IllegalArgumentException: Host class p/NestedUnsafe2 and anonymous class q/TestClass")) { + throw new RuntimeException("Exception: " + iae.toString()); + } + } + } +} --- /dev/null 2016-09-01 07:57:02.195527988 -0400 +++ new/test/runtime/defineAnonClass/TestBadHostClass.java 2016-09-02 08:40:14.866555077 -0400 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016, 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 8058575 + * @summary Test that bad host classes cause exceptions to get thrown. + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.base/jdk.internal.org.objectweb.asm + * @run main TestBadHostClass + */ + + +import java.lang.*; +import java.lang.reflect.Field; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.unsafe.UnsafeHelper; +import jdk.internal.org.objectweb.asm.ClassWriter; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +// Test that bad host classes cause exceptions. +public class TestBadHostClass { + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + private static String packageName(Class c) { + if (c.isArray()) { + return packageName(c.getComponentType()); + } else { + String name = c.getName(); + int dot = name.lastIndexOf('.'); + if (dot == -1) return ""; + return name.substring(0, dot); + } + } + + private static int constantPoolSize(byte[] classFile) { + return ((classFile[8] & 0xFF) << 8) | (classFile[9] & 0xFF); + } + + static public void badHostClass(Class hostClass) { + // choose a class name in the same package as the host class + String className; + if (hostClass != null) { + String prefix = packageName(hostClass); + if (prefix.length() > 0) + prefix = prefix.replace('.', '/') + "/"; + className = prefix + "Anon"; + } else { + className = "Anon"; + } + + // create the class + String superName = "java/lang/Object"; + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + + ClassWriter.COMPUTE_FRAMES); + cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, + className, null, superName, null); + byte[] classBytes = cw.toByteArray(); + int cpPoolSize = constantPoolSize(classBytes); + Class anonClass + = unsafe.defineAnonymousClass(hostClass, classBytes, new Object[cpPoolSize]); + } + + public static void main(String args[]) throws Exception { + // host class is an array of java.lang.Objects. + try { + badHostClass(Object[].class); + } catch (IllegalArgumentException ex) { + } + + // host class is an array of objects of this class. + try { + badHostClass(TestBadHostClass[].class); + } catch (IllegalArgumentException ex) { + } + + // host class is null. + try { + badHostClass(null); + } catch (NullPointerException ex) { + } + + // host class is a primitive array class. + try { + badHostClass(int[].class); + } catch (IllegalArgumentException ex) { + } + } +}