mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +02:00
8199940: Print more information about class loaders in IllegalAccessErrors
Reviewed-by: lfoltan, mchung
This commit is contained in:
parent
1e4a26ceda
commit
cad47f4a03
10 changed files with 762 additions and 38 deletions
|
@ -4612,12 +4612,17 @@ static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) {
|
|||
InstanceKlass::cast(super),
|
||||
vca_result);
|
||||
if (msg == NULL) {
|
||||
bool same_module = (this_klass->module() == super->module());
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"class %s cannot access its superclass %s",
|
||||
"class %s cannot access its %ssuperclass %s (%s%s%s)",
|
||||
this_klass->external_name(),
|
||||
super->external_name());
|
||||
super->is_abstract() ? "abstract " : "",
|
||||
super->external_name(),
|
||||
(same_module) ? this_klass->joint_in_module_of_loader(super) : this_klass->class_in_module_of_loader(),
|
||||
(same_module) ? "" : "; ",
|
||||
(same_module) ? "" : super->class_in_module_of_loader());
|
||||
} else {
|
||||
// Add additional message content.
|
||||
Exceptions::fthrow(
|
||||
|
@ -4646,12 +4651,16 @@ static void check_super_interface_access(const InstanceKlass* this_klass, TRAPS)
|
|||
InstanceKlass::cast(k),
|
||||
vca_result);
|
||||
if (msg == NULL) {
|
||||
bool same_module = (this_klass->module() == k->module());
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"class %s cannot access its superinterface %s",
|
||||
"class %s cannot access its superinterface %s (%s%s%s)",
|
||||
this_klass->external_name(),
|
||||
k->external_name());
|
||||
k->external_name(),
|
||||
(same_module) ? this_klass->joint_in_module_of_loader(k) : this_klass->class_in_module_of_loader(),
|
||||
(same_module) ? "" : "; ",
|
||||
(same_module) ? "" : k->class_in_module_of_loader());
|
||||
} else {
|
||||
// Add additional message content.
|
||||
Exceptions::fthrow(
|
||||
|
|
|
@ -294,7 +294,7 @@ void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
|
|||
base_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass();
|
||||
}
|
||||
// The element type could be a typeArray - we only need the access
|
||||
// check if it is an reference to another class.
|
||||
// check if it is a reference to another class.
|
||||
if (!base_klass->is_instance_klass()) {
|
||||
return; // no relevant check to do
|
||||
}
|
||||
|
@ -306,13 +306,17 @@ void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
|
|||
char* msg = Reflection::verify_class_access_msg(ref_klass,
|
||||
InstanceKlass::cast(base_klass),
|
||||
vca_result);
|
||||
bool same_module = (base_klass->module() == ref_klass->module());
|
||||
if (msg == NULL) {
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"failed to access class %s from class %s",
|
||||
"failed to access class %s from class %s (%s%s%s)",
|
||||
base_klass->external_name(),
|
||||
ref_klass->external_name());
|
||||
ref_klass->external_name(),
|
||||
(same_module) ? base_klass->joint_in_module_of_loader(ref_klass) : base_klass->class_in_module_of_loader(),
|
||||
(same_module) ? "" : "; ",
|
||||
(same_module) ? "" : ref_klass->class_in_module_of_loader());
|
||||
} else {
|
||||
// Use module specific message returned by verify_class_access_msg().
|
||||
Exceptions::fthrow(
|
||||
|
@ -596,8 +600,11 @@ void LinkResolver::check_method_accessability(Klass* ref_klass,
|
|||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"class %s tried to access method %s.%s%s (%s%s%s)",
|
||||
"class %s tried to access %s%s%smethod %s.%s%s (%s%s%s)",
|
||||
ref_klass->external_name(),
|
||||
sel_method->is_abstract() ? "abstract " : "",
|
||||
sel_method->is_protected() ? "protected " : "",
|
||||
sel_method->is_private() ? "private " : "",
|
||||
sel_klass->external_name(),
|
||||
sel_method->name()->as_C_string(),
|
||||
sel_method->signature()->as_C_string(),
|
||||
|
@ -927,14 +934,20 @@ void LinkResolver::check_field_accessability(Klass* ref_klass,
|
|||
// Any existing exceptions that may have been thrown, for example LinkageErrors
|
||||
// from nest-host resolution, have been allowed to propagate.
|
||||
if (!can_access) {
|
||||
bool same_module = (sel_klass->module() == ref_klass->module());
|
||||
ResourceMark rm(THREAD);
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"tried to access field %s.%s from class %s",
|
||||
"class %s tried to access %s%sfield %s.%s (%s%s%s)",
|
||||
ref_klass->external_name(),
|
||||
fd.is_protected() ? "protected " : "",
|
||||
fd.is_private() ? "private " : "",
|
||||
sel_klass->external_name(),
|
||||
fd.name()->as_C_string(),
|
||||
ref_klass->external_name()
|
||||
(same_module) ? ref_klass->joint_in_module_of_loader(sel_klass) : ref_klass->class_in_module_of_loader(),
|
||||
(same_module) ? "" : "; ",
|
||||
(same_module) ? "" : sel_klass->class_in_module_of_loader()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -671,7 +671,7 @@ public class TestNestmateMembership {
|
|||
static void test_NoHostInvoke() throws Throwable {
|
||||
System.out.println("Testing for missing nest-host attribute");
|
||||
String msg = "class TestNestmateMembership$Caller tried to access " +
|
||||
"method TestNestmateMembership$TargetNoHost.m()V";
|
||||
"private method TestNestmateMembership$TargetNoHost.m()V";
|
||||
try {
|
||||
Caller.invokeTargetNoHost();
|
||||
throw new Error("Missing IllegalAccessError: " + msg);
|
||||
|
@ -698,7 +698,7 @@ public class TestNestmateMembership {
|
|||
}
|
||||
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access " +
|
||||
"method TestNestmateMembership$Target.m()V";
|
||||
"private method TestNestmateMembership$Target.m()V";
|
||||
try {
|
||||
CallerNoHost.invokeTarget();
|
||||
throw new Error("Missing IllegalAccessError: " + msg);
|
||||
|
@ -706,8 +706,8 @@ public class TestNestmateMembership {
|
|||
catch (IllegalAccessError expected) {
|
||||
check_expected(expected, msg);
|
||||
}
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access method " +
|
||||
"TestNestmateMembership$TargetNoHost.m()V";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"method TestNestmateMembership$TargetNoHost.m()V";
|
||||
try {
|
||||
CallerNoHost.invokeTargetNoHost();
|
||||
throw new Error("Missing IllegalAccessError: " + msg);
|
||||
|
@ -949,8 +949,8 @@ public class TestNestmateMembership {
|
|||
|
||||
static void test_NoHostConstruct() throws Throwable {
|
||||
System.out.println("Testing for missing nest-host attribute");
|
||||
String msg = "class TestNestmateMembership$Caller tried to access method " +
|
||||
"TestNestmateMembership$TargetNoHost.<init>()V";
|
||||
String msg = "class TestNestmateMembership$Caller tried to access private " +
|
||||
"method TestNestmateMembership$TargetNoHost.<init>()V";
|
||||
try {
|
||||
Caller.newTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -976,8 +976,8 @@ public class TestNestmateMembership {
|
|||
check_expected(expected, msg);
|
||||
}
|
||||
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access method " +
|
||||
"TestNestmateMembership$Target.<init>()V";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"method TestNestmateMembership$Target.<init>()V";
|
||||
try {
|
||||
CallerNoHost.newTarget();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -985,8 +985,8 @@ public class TestNestmateMembership {
|
|||
catch (IncompatibleClassChangeError expected) {
|
||||
check_expected(expected, msg);
|
||||
}
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access method " +
|
||||
"TestNestmateMembership$TargetNoHost.<init>()V";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"method TestNestmateMembership$TargetNoHost.<init>()V";
|
||||
try {
|
||||
CallerNoHost.newTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1220,8 +1220,8 @@ public class TestNestmateMembership {
|
|||
|
||||
static void test_NoHostGetField() throws Throwable {
|
||||
System.out.println("Testing for missing nest-host attribute");
|
||||
String msg = "tried to access field TestNestmateMembership$TargetNoHost.f" +
|
||||
" from class TestNestmateMembership$Caller";
|
||||
String msg = "class TestNestmateMembership$Caller tried to access private " +
|
||||
"field TestNestmateMembership$TargetNoHost.f";
|
||||
try {
|
||||
Caller.getFieldTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1247,8 +1247,8 @@ public class TestNestmateMembership {
|
|||
check_expected(expected, msg);
|
||||
}
|
||||
|
||||
msg = "tried to access field TestNestmateMembership$Target.f" +
|
||||
" from class TestNestmateMembership$CallerNoHost";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"field TestNestmateMembership$Target.f";
|
||||
try {
|
||||
CallerNoHost.getFieldTarget();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1256,8 +1256,8 @@ public class TestNestmateMembership {
|
|||
catch (IncompatibleClassChangeError expected) {
|
||||
check_expected(expected, msg);
|
||||
}
|
||||
msg = "tried to access field TestNestmateMembership$TargetNoHost.f" +
|
||||
" from class TestNestmateMembership$CallerNoHost";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"field TestNestmateMembership$TargetNoHost.f";
|
||||
try {
|
||||
CallerNoHost.getFieldTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1485,8 +1485,8 @@ public class TestNestmateMembership {
|
|||
|
||||
static void test_NoHostPutField() throws Throwable {
|
||||
System.out.println("Testing for missing nest-host attribute");
|
||||
String msg = "tried to access field TestNestmateMembership$TargetNoHost.f" +
|
||||
" from class TestNestmateMembership$Caller";
|
||||
String msg = "class TestNestmateMembership$Caller tried to access private " +
|
||||
"field TestNestmateMembership$TargetNoHost.f";
|
||||
try {
|
||||
Caller.putFieldTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1512,8 +1512,8 @@ public class TestNestmateMembership {
|
|||
check_expected(expected, msg);
|
||||
}
|
||||
|
||||
msg = "tried to access field TestNestmateMembership$Target.f" +
|
||||
" from class TestNestmateMembership$CallerNoHost";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"field TestNestmateMembership$Target.f";
|
||||
try {
|
||||
CallerNoHost.putFieldTarget();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
@ -1521,8 +1521,8 @@ public class TestNestmateMembership {
|
|||
catch (IncompatibleClassChangeError expected) {
|
||||
check_expected(expected, msg);
|
||||
}
|
||||
msg = "tried to access field TestNestmateMembership$TargetNoHost.f" +
|
||||
" from class TestNestmateMembership$CallerNoHost";
|
||||
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
|
||||
"field TestNestmateMembership$TargetNoHost.f";
|
||||
try {
|
||||
CallerNoHost.putFieldTargetNoHost();
|
||||
throw new Error("Missing IncompatibleClassChangeError: " + msg);
|
||||
|
|
|
@ -51,7 +51,7 @@ public class TestConstructorHierarchy {
|
|||
throw new Error("Unexpected construction of ExternalSuper");
|
||||
}
|
||||
catch (IllegalAccessError iae) {
|
||||
if (iae.getMessage().contains("class TestConstructorHierarchy tried to access method ExternalSuper.<init>()V")) {
|
||||
if (iae.getMessage().contains("class TestConstructorHierarchy tried to access private method ExternalSuper.<init>()V")) {
|
||||
System.out.println("Got expected exception constructing ExternalSuper: " + iae);
|
||||
}
|
||||
else throw new Error("Unexpected IllegalAccessError: " + iae);
|
||||
|
@ -61,7 +61,7 @@ public class TestConstructorHierarchy {
|
|||
throw new Error("Unexpected construction of NestedA and supers");
|
||||
}
|
||||
catch (IllegalAccessError iae) {
|
||||
if (iae.getMessage().contains("class TestConstructorHierarchy$NestedA tried to access method ExternalSuper.<init>()V")) {
|
||||
if (iae.getMessage().contains("class TestConstructorHierarchy$NestedA tried to access private method ExternalSuper.<init>()V")) {
|
||||
System.out.println("Got expected exception constructing NestedA: " + iae);
|
||||
}
|
||||
else throw new Error("Unexpected IllegalAccessError: " + iae);
|
||||
|
@ -71,7 +71,7 @@ public class TestConstructorHierarchy {
|
|||
throw new Error("Unexpected construction of ExternalSub");
|
||||
}
|
||||
catch (IllegalAccessError iae) {
|
||||
if (iae.getMessage().contains("class ExternalSub tried to access method TestConstructorHierarchy$NestedA.<init>()V")) {
|
||||
if (iae.getMessage().contains("class ExternalSub tried to access private method TestConstructorHierarchy$NestedA.<init>()V")) {
|
||||
System.out.println("Got expected exception constructing ExternalSub: " + iae);
|
||||
}
|
||||
else throw new Error("Unexpected IllegalAccessError: " + iae);
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2018 by SAP AG, Walldorf, Germany.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
public class IAE78_A {
|
||||
|
||||
static Object f = new Object();
|
||||
|
||||
IAE78_A() {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
public class IAE78_B {
|
||||
|
||||
public static void create() {
|
||||
new IAE78_A();
|
||||
}
|
||||
|
||||
public static void access() {
|
||||
IAE78_A.f.hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
public class IAE_Loader1 extends ClassLoader {
|
||||
|
||||
private final Set<String> names = new HashSet<>();
|
||||
|
||||
public IAE_Loader1(String name, String[] names) {
|
||||
super(name, ClassLoader.getSystemClassLoader());
|
||||
for (String n : names) this.names.add(n);
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
if (!names.contains(name)) {
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
String filename = name.replace('.', '/') + ".class";
|
||||
Class<?> result = null;
|
||||
try (InputStream data = getResourceAsStream(filename)) {
|
||||
if (data == null) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
|
||||
byte[] bytes = data.readAllBytes();
|
||||
result = defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ClassNotFoundException("Error reading class file", e);
|
||||
}
|
||||
if (resolve) resolveClass(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2018 by SAP AG, Walldorf, Germany.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.SecureClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This is a class loader which can load the same classes as another class loader.
|
||||
* <p>
|
||||
* This is mainly useful for tests when you want to load a class, but do it with a class
|
||||
* loader you can dispose. The clone loader just asks the loader to be cloned to get
|
||||
* the bytecodes, but defines the class itself.
|
||||
* <p>
|
||||
* Additionally you can specify a set of classes the loader should not be able to load.
|
||||
*/
|
||||
public class IAE_Loader2 extends SecureClassLoader {
|
||||
|
||||
/**
|
||||
* The class loaded to clone.
|
||||
*/
|
||||
private final ClassLoader toClone;
|
||||
|
||||
/**
|
||||
* The strings we cannot load.
|
||||
*/
|
||||
private final HashSet<String> notLoadable;
|
||||
|
||||
/**
|
||||
* The strings we just delegate.
|
||||
*/
|
||||
private final HashSet<String> simpleDelegate;
|
||||
|
||||
/**
|
||||
* Creates a class loader which can load the same classes as the loader which
|
||||
* loaded the <code>IAE_Loader2</code> class itself.
|
||||
* <p>
|
||||
* Only the classes which are loadable by the 'parent' loader are delegated to that
|
||||
* loader (to make it possible mix classes).
|
||||
*
|
||||
* @param name the name of the class loader.
|
||||
* @param parent the parent loader which is first asked to load a class.
|
||||
* @param toClone the class loader to mimic. The clone class loader will be able to
|
||||
* load the same classes as the 'toClone' loader.
|
||||
* @param notLoadable The classes we should not be able to load via this loader.
|
||||
* @param simpleDelegate The names of the classes for which we simply delegate.
|
||||
*/
|
||||
public IAE_Loader2(String name, ClassLoader parent, ClassLoader toClone,
|
||||
String[] notLoadable, String[] simpleDelegate) {
|
||||
super(name, parent);
|
||||
|
||||
this.toClone = toClone;
|
||||
this.notLoadable = Arrays.stream(notLoadable).collect(Collectors.toCollection(HashSet<String>::new));
|
||||
this.simpleDelegate = Arrays.stream(simpleDelegate).collect(Collectors.toCollection(HashSet<String>::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.ClassLoader#findClass(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
if (notLoadable.contains(name)) {
|
||||
throw new ClassNotFoundException("The clone class loader explicitely " +
|
||||
"didn't found the class");
|
||||
}
|
||||
|
||||
if (simpleDelegate.contains(name)) {
|
||||
return toClone.loadClass(name);
|
||||
}
|
||||
|
||||
// We just ask the wrapper class loader to find the resource for us
|
||||
URL res = toClone.getResource(name.replace('.', '/') + ".class");
|
||||
|
||||
if (res == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream is = res.openStream();
|
||||
byte[] code = readStreamIntoBuffer(is, 8192);
|
||||
is.close();
|
||||
return defineClass(name, code, 0, code.length);
|
||||
} catch (IOException e) {
|
||||
throw new ClassNotFoundException(name, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all data of a stream into a byte array. The method allocates as
|
||||
* much memory as necessary to put the whole data into that byte
|
||||
* array. The data is read in chunks of <code>chunkSize</code>
|
||||
* chunks.<br><br>
|
||||
* <b>Implementation Note: </b> The data is read in chunks of
|
||||
* <code>chunkSize</code> bytes. The data is copied to the result
|
||||
* array. The memory consumption at the end of the reading is
|
||||
* <code>2 x [size of resulting array] + chunkSize</code>.
|
||||
*
|
||||
* @param is the stream to read the data from
|
||||
* @param chunkSize the size of the chunks the data should be read in
|
||||
* @return the <b>whole</b> data of the stream read into an byte array
|
||||
* @throws IllegalArgumentException if chunkSize <= 0
|
||||
* @throws NullPointerException if is == null
|
||||
* @throws IOException thrown if the provided stream encounters IO problems
|
||||
*/
|
||||
public static byte[] readStreamIntoBuffer(InputStream is, int chunkSize)
|
||||
throws IOException {
|
||||
|
||||
// Check preconditions.
|
||||
if (chunkSize <= 0) {
|
||||
throw new IllegalArgumentException("chunkSize <= 0");
|
||||
}
|
||||
else if (is == null) {
|
||||
throw new NullPointerException("is is null");
|
||||
}
|
||||
|
||||
// Temporary buffer for read operations and result buffer.
|
||||
byte[] tempBuffer = new byte[chunkSize];
|
||||
byte[] buffer = new byte[0];
|
||||
|
||||
int bytesRead = 0; // bytes actual read
|
||||
int oldSize = 0; // size of the resulting buffer
|
||||
|
||||
while ((bytesRead = is.read(tempBuffer)) > 0) {
|
||||
|
||||
// Temporary reference to the buffer for the copy operation.
|
||||
byte[] oldBuffer = buffer;
|
||||
|
||||
// Create a new buffer with the size needed and copy data.
|
||||
buffer = new byte[oldSize + bytesRead];
|
||||
System.arraycopy(oldBuffer, 0, buffer, 0, oldBuffer.length);
|
||||
|
||||
// Copy the new data.
|
||||
System.arraycopy(tempBuffer, 0, buffer, oldSize, bytesRead);
|
||||
oldSize += bytesRead;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. 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
|
||||
* @summary Test messages of IllegalAccessError.
|
||||
* @modules java.base/java.lang:open
|
||||
* java.base/jdk.internal.org.objectweb.asm
|
||||
* @compile IAE_Loader1.java IAE_Loader2.java IAE78_A.java IAE78_B.java
|
||||
* IllegalAccessErrorTest.java
|
||||
* @run main/othervm -Xbootclasspath/a:. test.IllegalAccessErrorTest
|
||||
*/
|
||||
|
||||
// Put this test into a package so we see qualified class names in
|
||||
// the error messages. Verify that classes are printed with '.' instead
|
||||
// of '/'.
|
||||
package test;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandles.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
import java.security.*;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import test.*;
|
||||
|
||||
abstract public class IllegalAccessErrorTest {
|
||||
|
||||
// interface
|
||||
private static String expectedErrorMessage1a_1 =
|
||||
"class test.IAE1_B cannot access its superinterface test.IAE1_A " +
|
||||
"(test.IAE1_B is in unnamed module of loader test.IAE_Loader1 @";
|
||||
private static String expectedErrorMessage1a_2 =
|
||||
"; test.IAE1_A is in unnamed module of loader 'app')";
|
||||
private static String expectedErrorMessage1b_1 =
|
||||
"class test.IAE1_B cannot access its superinterface test.IAE1_A " +
|
||||
"(test.IAE1_B is in unnamed module of loader 'someCLName1' @";
|
||||
private static String expectedErrorMessage1b_2 =
|
||||
"; test.IAE1_A is in unnamed module of loader 'app')";
|
||||
|
||||
// abstract class
|
||||
private static String expectedErrorMessage2_1 =
|
||||
"class test.IAE2_B cannot access its abstract superclass test.IAE2_A " +
|
||||
"(test.IAE2_B is in unnamed module of loader 'someCLName2' @";
|
||||
private static String expectedErrorMessage2_2 =
|
||||
"; test.IAE2_A is in unnamed module of loader 'app')";
|
||||
|
||||
// class
|
||||
private static String expectedErrorMessage3_1 =
|
||||
"class test.IAE3_B cannot access its superclass test.IAE3_A " +
|
||||
"(test.IAE3_B is in unnamed module of loader 'someCLName3' @";
|
||||
private static String expectedErrorMessage3_2 =
|
||||
"; test.IAE3_A is in unnamed module of loader 'app')";
|
||||
|
||||
public static void test123(String loaderName,
|
||||
String expectedErrorMessage_1,
|
||||
String expectedErrorMessage_2,
|
||||
String testClass) throws Exception {
|
||||
String[] classNames = { testClass };
|
||||
// Some classes under a new Loader.
|
||||
ClassLoader l = new IAE_Loader1(loaderName, classNames);
|
||||
|
||||
try {
|
||||
l.loadClass(testClass);
|
||||
throw new RuntimeException("Expected IllegalAccessError was not thrown.");
|
||||
} catch (IllegalAccessError iae) {
|
||||
String errorMsg = iae.getMessage();
|
||||
if (!(errorMsg.contains(expectedErrorMessage_1) &&
|
||||
errorMsg.contains(expectedErrorMessage_2))) {
|
||||
System.out.println("Expected: " + expectedErrorMessage_1 + "@id " + expectedErrorMessage_2 +"\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
} else {
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a class file with the given class name. The class implements Runnable
|
||||
// with a run method to invokestatic the given targetClass/targetMethod.
|
||||
static byte[] iae4_generateRunner(String className,
|
||||
String targetClass,
|
||||
String targetMethod) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
new String[] { "java/lang/Runnable" });
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// run()
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
|
||||
mv.visitMethodInsn(INVOKESTATIC, tc, targetMethod, "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
// Private method that should raise IllegalAccessError when called.
|
||||
private static void iae4_m() { }
|
||||
|
||||
private static String expectedErrorMessage4 =
|
||||
"class test.Runner4 tried to access private method test.IllegalAccessErrorTest.iae4_m()V " +
|
||||
"(test.Runner4 and test.IllegalAccessErrorTest are in unnamed module of loader 'app')";
|
||||
|
||||
// Test according to java/lang/invoke/DefineClassTest.java
|
||||
public static void test4_privateMethod() throws Exception {
|
||||
final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName();
|
||||
final String THIS_CLASS = IllegalAccessErrorTest.class.getName();
|
||||
final String CLASS_NAME = THIS_PACKAGE + ".Runner4";
|
||||
Lookup lookup = lookup();
|
||||
|
||||
// private
|
||||
byte[] classBytes = iae4_generateRunner(CLASS_NAME, THIS_CLASS, "iae4_m");
|
||||
Class<?> clazz = lookup.defineClass(classBytes);
|
||||
Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance();
|
||||
try {
|
||||
r.run();
|
||||
throw new RuntimeException("Expected IllegalAccessError was not thrown.");
|
||||
} catch (IllegalAccessError exc) {
|
||||
String errorMsg = exc.getMessage();
|
||||
if (!errorMsg.equals(expectedErrorMessage4)) {
|
||||
System.out.println("Expected: " + expectedErrorMessage4 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
}
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a class file with the given class name. The class implements Runnable
|
||||
// with a run method to invokestatic the given targetClass/targetField.
|
||||
static byte[] iae5_generateRunner(String className,
|
||||
String targetClass,
|
||||
String targetField) throws Exception {
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V9,
|
||||
ACC_PUBLIC + ACC_SUPER,
|
||||
className.replace(".", "/"),
|
||||
null,
|
||||
"java/lang/Object",
|
||||
new String[] { "java/lang/Runnable" });
|
||||
|
||||
// <init>
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
// run()
|
||||
String tc = targetClass.replace(".", "/");
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null);
|
||||
mv.visitFieldInsn(GETSTATIC, tc, targetField, "I");
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
// Private field that should raise IllegalAccessError when accessed.
|
||||
private static int iae5_f = 77;
|
||||
|
||||
private static String expectedErrorMessage5 =
|
||||
"class test.Runner5 tried to access private field test.IllegalAccessErrorTest.iae5_f " +
|
||||
"(test.Runner5 and test.IllegalAccessErrorTest are in unnamed module of loader 'app')";
|
||||
|
||||
// Test according to java/lang/invoke/DefineClassTest.java
|
||||
public static void test5_privateField() throws Exception {
|
||||
final String THIS_PACKAGE = IllegalAccessErrorTest.class.getPackageName();
|
||||
final String THIS_CLASS = IllegalAccessErrorTest.class.getName();
|
||||
final String CLASS_NAME = THIS_PACKAGE + ".Runner5";
|
||||
Lookup lookup = lookup();
|
||||
|
||||
// private
|
||||
byte[] classBytes = iae5_generateRunner(CLASS_NAME, THIS_CLASS, "iae5_f");
|
||||
Class<?> clazz = lookup.defineClass(classBytes);
|
||||
Runnable r = (Runnable) clazz.getDeclaredConstructor().newInstance();
|
||||
try {
|
||||
r.run();
|
||||
throw new RuntimeException("Expected IllegalAccessError was not thrown.");
|
||||
} catch (IllegalAccessError exc) {
|
||||
String errorMsg = exc.getMessage();
|
||||
if (!errorMsg.equals(expectedErrorMessage5)) {
|
||||
System.out.println("Expected: " + expectedErrorMessage5 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
}
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessage6 =
|
||||
"failed to access class test.IAE6_A from class test.IAE6_B " +
|
||||
"(test.IAE6_A is in unnamed module of loader 'app'; test.IAE6_B is in unnamed module of loader 'test6_class_CL' @";
|
||||
|
||||
public static void test6_class() throws Exception {
|
||||
ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
|
||||
IAE_Loader2 loader = new IAE_Loader2("test6_class_CL", base.getParent(), base, new String[0],
|
||||
new String[] { IAE6_A.class.getName() });
|
||||
Class<?> cl = loader.loadClass(IAE6_B.class.getName());
|
||||
Method m = cl.getDeclaredMethod("create", new Class[0]);
|
||||
m.setAccessible(true);
|
||||
|
||||
try {
|
||||
m.invoke(null, new Object[0]);
|
||||
throw new RuntimeException("Expected IllegalAccessError was not thrown.");
|
||||
} catch (InvocationTargetException e) {
|
||||
IllegalAccessError iae = (IllegalAccessError) e.getCause();
|
||||
String errorMsg = iae.getMessage();
|
||||
if (!errorMsg.contains(expectedErrorMessage6)) {
|
||||
System.out.println("Expected: " + expectedErrorMessage6 + "id)\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
}
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessage7_1 =
|
||||
"class test.IAE78_B tried to access method test.IAE78_A.<init>()V " +
|
||||
"(test.IAE78_B is in unnamed module of loader 'test7_method_CL' @";
|
||||
private static String expectedErrorMessage7_2 =
|
||||
"; test.IAE78_A is in unnamed module of loader 'app')";
|
||||
|
||||
// Similar to test4.
|
||||
public static void test7_method() throws Exception {
|
||||
ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
|
||||
IAE_Loader2 loader = new IAE_Loader2("test7_method_CL", base.getParent(), base, new String[0],
|
||||
new String[] {IAE78_A.class.getName()});
|
||||
Class<?> cl = loader.loadClass(IAE78_B.class.getName());
|
||||
Method m = cl.getDeclaredMethod("create", new Class[0]);
|
||||
|
||||
try {
|
||||
m.invoke(null, new Object[0]);
|
||||
} catch (InvocationTargetException e) {
|
||||
IllegalAccessError iae = (IllegalAccessError) e.getCause();
|
||||
String errorMsg = iae.getMessage();
|
||||
if (!(errorMsg.contains(expectedErrorMessage7_1) &&
|
||||
errorMsg.contains(expectedErrorMessage7_2))) {
|
||||
System.out.println("Expected: " + expectedErrorMessage7_1 + "id" + expectedErrorMessage7_2 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
}
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static String expectedErrorMessage8_1 =
|
||||
"class test.IAE78_B tried to access field test.IAE78_A.f " +
|
||||
"(test.IAE78_B is in unnamed module of loader 'test8_field_CL' @";
|
||||
private static String expectedErrorMessage8_2 =
|
||||
"; test.IAE78_A is in unnamed module of loader 'app')";
|
||||
|
||||
// Similar to test5.
|
||||
public static void test8_field() throws Exception {
|
||||
ClassLoader base = IllegalAccessErrorTest.class.getClassLoader();
|
||||
IAE_Loader2 loader = new IAE_Loader2("test8_field_CL", base.getParent(), base, new String[0],
|
||||
new String[] { IAE78_A.class.getName() });
|
||||
Class<?> cl = loader.loadClass(IAE78_B.class.getName());
|
||||
Method m = cl.getDeclaredMethod("access", new Class[0]);
|
||||
|
||||
try {
|
||||
m.invoke(null, new Object[0]);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
IllegalAccessError iae = (IllegalAccessError) e.getCause();
|
||||
String errorMsg = iae.getMessage();
|
||||
if (!(errorMsg.contains(expectedErrorMessage8_1) &&
|
||||
errorMsg.contains(expectedErrorMessage8_2))) {
|
||||
System.out.println("Expected: " + expectedErrorMessage8_1 + "id" + expectedErrorMessage8_2 + "\n" +
|
||||
"but got: " + errorMsg);
|
||||
throw new RuntimeException("Wrong error message of IllegalAccessError.");
|
||||
}
|
||||
System.out.println("Passed with message: " + errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
test123(null, expectedErrorMessage1a_1, expectedErrorMessage1a_2, "test.IAE1_B"); // interface
|
||||
test123("someCLName1", expectedErrorMessage1b_1, expectedErrorMessage1b_2, "test.IAE1_B"); // interface
|
||||
test123("someCLName2", expectedErrorMessage2_1, expectedErrorMessage2_2, "test.IAE2_B"); // abstract class
|
||||
test123("someCLName3", expectedErrorMessage3_1, expectedErrorMessage3_2, "test.IAE3_B"); // class
|
||||
test4_privateMethod();
|
||||
test5_privateField();
|
||||
test6_class();
|
||||
test7_method();
|
||||
test8_field();
|
||||
}
|
||||
}
|
||||
|
||||
// Class hierarchies for test1.
|
||||
interface IAE1_A {
|
||||
public IAE1_D gen();
|
||||
}
|
||||
|
||||
class IAE1_B implements IAE1_A {
|
||||
public IAE1_D gen() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IAE1_C {
|
||||
}
|
||||
|
||||
class IAE1_D extends IAE1_C {
|
||||
}
|
||||
|
||||
|
||||
// Class hierarchies for test2.
|
||||
abstract class IAE2_A {
|
||||
abstract public IAE2_D gen();
|
||||
}
|
||||
|
||||
class IAE2_B extends IAE2_A {
|
||||
public IAE2_D gen() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IAE2_C {
|
||||
}
|
||||
|
||||
class IAE2_D extends IAE2_C {
|
||||
}
|
||||
|
||||
|
||||
// Class hierarchies for test3.
|
||||
class IAE3_A {
|
||||
public IAE3_D gen() {
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
class IAE3_B extends IAE3_A {
|
||||
public IAE3_D gen() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IAE3_C {
|
||||
}
|
||||
|
||||
class IAE3_D extends IAE3_C {
|
||||
}
|
||||
|
||||
|
||||
// Class hierarchies for test6.
|
||||
class IAE6_A {
|
||||
IAE6_A() {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
class IAE6_B {
|
||||
public static void create() {
|
||||
new IAE6_A();
|
||||
}
|
||||
}
|
|
@ -103,9 +103,10 @@ public class ExpQualToM1PrivateMethodIAE {
|
|||
String message = e.getMessage();
|
||||
System.out.println(e.toString());
|
||||
// java.lang.IllegalAccessError:
|
||||
// tried to access method p2.c2.method2()V from class p1.c1 (p2.c2 is in module m2x of loader
|
||||
// tried to access private method p2.c2.method2()V from class p1.c1 (p2.c2 is in module m2x of loader
|
||||
// myloaders.MySameClassLoader @<id>; p1.c1 is in module m1x of loader myloaders.MySameClassLoader @<id>)
|
||||
if (!message.contains("class p1.c1 tried to access method p2.c2.method2()V (p1.c1 is in module m1x of loader myloaders.MySameClassLoader @") ||
|
||||
if (!message.contains("class p1.c1 tried to access private method p2.c2.method2()V " +
|
||||
"(p1.c1 is in module m1x of loader myloaders.MySameClassLoader @") ||
|
||||
!message.contains("; p2.c2 is in module m2x of loader myloaders.MySameClassLoader @")) {
|
||||
throw new RuntimeException("Test Failed, an IAE was thrown with the wrong message: " + e.toString());
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue