8058575: IllegalAccessError trying to access package-private class from VM anonymous class

Put anonymous classes in unnamed package into host class's package.  Throw exception if host class's package differs from anonymous class.

Reviewed-by: coleenp, acorn
This commit is contained in:
Harold Seigel 2016-09-07 07:19:48 -04:00
parent c271d83599
commit e8e6415b7a
14 changed files with 416 additions and 31 deletions

View File

@ -5407,6 +5407,59 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
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::new_symbol(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()));
@ -5422,7 +5475,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
Publicity pub_level,
TRAPS) :
@ -5697,6 +5750,13 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
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");

View File

@ -79,7 +79,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
const Symbol* _requested_name;
Symbol* _class_name;
mutable ClassLoaderData* _loader_data;
const Klass* _host_klass;
const InstanceKlass* _host_klass;
GrowableArray<Handle>* _cp_patches; // overrides for CP entries
// Metadata created before the instance klass is created. Must be deallocated
@ -155,6 +155,9 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
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 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
Publicity pub_level,
TRAPS);
@ -500,7 +503,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
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<Handle>* cp_patches() const { return _cp_patches; }
ClassLoaderData* loader_data() const { return _loader_data; }
const Symbol* class_name() const { return _class_name; }

View File

@ -94,7 +94,7 @@ instanceKlassHandle KlassFactory::create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS) {

View File

@ -72,7 +72,7 @@ class KlassFactory : AllStatic {
Symbol* name,
ClassLoaderData* loader_data,
Handle protection_domain,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS);
};

View File

@ -1027,7 +1027,7 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name,
Handle class_loader,
Handle protection_domain,
ClassFileStream* st,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS) {

View File

@ -299,7 +299,7 @@ public:
Handle class_loader,
Handle protection_domain,
ClassFileStream* st,
const Klass* host_klass,
const InstanceKlass* host_klass,
GrowableArray<Handle>* cp_patches,
TRAPS);

View File

@ -2786,7 +2786,7 @@ void ClassVerifier::verify_invoke_instructions(
// 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) {

View File

@ -619,8 +619,8 @@ class InstanceKlass: public Klass {
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 @@ class InstanceKlass: public Klass {
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 @@ public:
}
};
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;

View File

@ -783,6 +783,7 @@ UNSAFE_ENTRY(jclass, Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring na
// 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
@ -791,8 +792,12 @@ UNSAFE_ENTRY(jclass, Unsafe_DefineClass0(JNIEnv *env, jobject unsafe, jstring na
// 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
@ -874,9 +879,11 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
// 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());
@ -907,7 +914,7 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
host_loader,
host_domain,
&st,
host_klass,
InstanceKlass::cast(host_klass),
cp_patches,
CHECK_NULL);
if (anonk == NULL) {

View File

@ -412,13 +412,13 @@ oop Reflection::array_component_type(oop mirror, TRAPS) {
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 @@ static bool can_relax_access_check_for(const Klass* accessor,
// 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 &&

View File

@ -62,7 +62,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
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 class CallSiteDepContextTest {
}
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 @@ public class CallSiteDepContextTest {
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 @@ public class CallSiteDepContextTest {
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 @@ public class CallSiteDepContextTest {
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 {

View File

@ -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
}
}

View File

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

View File

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