undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// this_class is modified to CONSTANT_NameAndType_info
|
||||
class BadClassFile {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
58; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
NameAndType #5 #6; // #7
|
||||
Utf8 "BadClassFile"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFile.java"; // #12
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFile
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// this_class is modified to CONSTANT_String_info
|
||||
|
||||
class BadClassFile2 {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
58; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
String #13; // #7
|
||||
Utf8 "BadClassFile2"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFile2.java"; // #12
|
||||
String "BadClassFile2.java"; // #13
|
||||
Utf8 "bad/Package/ClassFile"; // #14
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFile2
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// major version modified to 100
|
||||
class BadClassFileVersion {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
100; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
class #8; // #7
|
||||
Utf8 "BadClassFileVersion"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFileVersion.java"; // #12
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFileVersion
|
||||
517
test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
Normal file
517
test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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 8330467
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib
|
||||
* @compile BadClassFile.jcod
|
||||
* BadClassFile2.jcod
|
||||
* BadClassFileVersion.jcod
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* @run junit/othervm BasicTest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import static java.lang.classfile.ClassFile.*;
|
||||
import static java.lang.constant.ConstantDescs.CD_Enum;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
interface HiddenTest {
|
||||
void test();
|
||||
}
|
||||
|
||||
public class BasicTest {
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
|
||||
private static final Path CLASSES_DIR = Paths.get("classes");
|
||||
private static final Path CLASSES_10_DIR = Paths.get("classes_10");
|
||||
|
||||
private static byte[] hiddenClassBytes;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() throws IOException {
|
||||
compileSources(SRC_DIR, CLASSES_DIR);
|
||||
hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
|
||||
|
||||
// compile with --release 10 with no NestHost and NestMembers attribute
|
||||
compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
|
||||
compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
|
||||
}
|
||||
|
||||
static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
|
||||
Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
|
||||
if (options != null && options.length > 0) {
|
||||
ops = Stream.concat(ops, Arrays.stream(options));
|
||||
}
|
||||
if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
|
||||
throw new RuntimeException("Compilation of the test failed: " + sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> defineHiddenClass(String name) throws Exception {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
assertHiddenClass(hc);
|
||||
singletonNest(hc);
|
||||
return hc;
|
||||
}
|
||||
|
||||
// basic test on a hidden class
|
||||
@Test
|
||||
public void hiddenClass() throws Throwable {
|
||||
HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
|
||||
t.test();
|
||||
|
||||
// sanity check
|
||||
Class<?> c = t.getClass();
|
||||
Class<?>[] intfs = c.getInterfaces();
|
||||
assertTrue(c.isHidden());
|
||||
assertFalse(c.isPrimitive());
|
||||
assertEquals(1, intfs.length);
|
||||
assertSame(HiddenTest.class, intfs[0]);
|
||||
assertNull(c.getCanonicalName());
|
||||
|
||||
String hcName = "HiddenClass";
|
||||
String hcSuffix = "0x[0-9a-f]+";
|
||||
assertTrue(c.getName().matches(hcName + "/" + hcSuffix));
|
||||
assertTrue(c.descriptorString().matches("L" + hcName + "." + hcSuffix + ";"), c.descriptorString());
|
||||
|
||||
// test array of hidden class
|
||||
testHiddenArray(c);
|
||||
|
||||
// test setAccessible
|
||||
checkSetAccessible(c, "realTest");
|
||||
checkSetAccessible(c, "test");
|
||||
}
|
||||
|
||||
// primitive class is not a hidden class
|
||||
@Test
|
||||
public void primitiveClass() {
|
||||
assertFalse(int.class.isHidden());
|
||||
assertFalse(String.class.isHidden());
|
||||
}
|
||||
|
||||
private void testHiddenArray(Class<?> type) throws Exception {
|
||||
// array of hidden class
|
||||
Object array = Array.newInstance(type, 2);
|
||||
Class<?> arrayType = array.getClass();
|
||||
assertTrue(arrayType.isArray());
|
||||
assertEquals(2, Array.getLength(array));
|
||||
assertFalse(arrayType.isHidden());
|
||||
|
||||
String hcName = "HiddenClass";
|
||||
String hcSuffix = "0x[0-9a-f]+";
|
||||
assertTrue(arrayType.getName().matches("\\[" + "L" + hcName + "/" + hcSuffix + ";"));
|
||||
assertTrue(arrayType.descriptorString().matches("\\[" + "L" + hcName + "." + hcSuffix + ";"));
|
||||
|
||||
assertTrue(arrayType.getComponentType().isHidden());
|
||||
assertSame(type, arrayType.getComponentType());
|
||||
Object t = type.newInstance();
|
||||
Array.set(array, 0, t);
|
||||
Object o = Array.get(array, 0);
|
||||
assertSame(t, o);
|
||||
}
|
||||
|
||||
private void checkSetAccessible(Class<?> c, String name, Class<?>... ptypes) throws Exception {
|
||||
Method m = c.getDeclaredMethod(name, ptypes);
|
||||
assertTrue(m.trySetAccessible());
|
||||
m.setAccessible(true);
|
||||
}
|
||||
|
||||
// Define a hidden class that uses lambda
|
||||
// This verifies LambdaMetaFactory supports the caller which is a hidden class
|
||||
@Test
|
||||
public void testLambda() throws Throwable {
|
||||
HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
|
||||
var e = assertThrows(Error.class, t::test);
|
||||
assertEquals("thrown by " + t.getClass().getName(), e.getMessage());
|
||||
}
|
||||
|
||||
// Define a hidden class that uses lambda and contains its implementation
|
||||
// This verifies LambdaMetaFactory supports the caller which is a hidden class
|
||||
@Test
|
||||
public void testHiddenLambda() throws Throwable {
|
||||
HiddenTest t = (HiddenTest)defineHiddenClass("HiddenLambda").newInstance();
|
||||
var e = assertThrows(Error.class, t::test);
|
||||
assertEquals("thrown by " + t.getClass().getName(), e.getMessage());
|
||||
}
|
||||
|
||||
// Verify the nest host and nest members of a hidden class and hidden nestmate class
|
||||
@Test
|
||||
public void testHiddenNestHost() throws Throwable {
|
||||
byte[] hc1 = hiddenClassBytes;
|
||||
Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
|
||||
Class<?> host = lookup1.lookupClass();
|
||||
|
||||
byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
|
||||
Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE);
|
||||
Class<?> member = lookup2.lookupClass();
|
||||
|
||||
// test nest membership and reflection API
|
||||
assertTrue(host.isNestmateOf(member));
|
||||
assertSame(host, host.getNestHost());
|
||||
// getNestHost and getNestMembers return the same value when calling
|
||||
// on a nest member and the nest host
|
||||
assertSame(host.getNestHost(), member.getNestHost());
|
||||
assertArrayEquals(member.getNestMembers(), host.getNestMembers());
|
||||
// getNestMembers includes the nest host that can be a hidden class but
|
||||
// only includes static nest members
|
||||
assertEquals(1, host.getNestMembers().length);
|
||||
assertSame(host, host.getNestMembers()[0]);
|
||||
}
|
||||
|
||||
private static Object[][] hiddenClasses() {
|
||||
return new Object[][] {
|
||||
new Object[] { "HiddenInterface", false },
|
||||
new Object[] { "AbstractClass", false },
|
||||
// a hidden annotation is useless because it cannot be referenced by any class
|
||||
new Object[] { "HiddenAnnotation", false },
|
||||
// class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute
|
||||
// define them as nestmate to verify Class::getNestHost and getNestMembers
|
||||
new Object[] { "Outer", true },
|
||||
new Object[] { "Outer$Inner", true },
|
||||
new Object[] { "EnclosingClass", true },
|
||||
new Object[] { "EnclosingClass$1", true },
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that class file bytes that can be defined as a normal class
|
||||
* can be successfully created as a hidden class even it might not
|
||||
* make sense as a hidden class. For example, a hidden annotation
|
||||
* is not useful as it cannot be referenced and an outer/inner class
|
||||
* when defined as a hidden effectively becomes a final top-level class.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("hiddenClasses")
|
||||
public void defineHiddenClass(String name, boolean nestmate) throws Exception {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
|
||||
Class<?> hc;
|
||||
Class<?> host;
|
||||
if (nestmate) {
|
||||
hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass();
|
||||
host = lookup().lookupClass().getNestHost();
|
||||
} else {
|
||||
hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
host = hc;
|
||||
}
|
||||
assertSame(host, hc.getNestHost());
|
||||
assertEquals(1, hc.getNestMembers().length);
|
||||
assertSame(host, hc.getNestMembers()[0]);
|
||||
}
|
||||
|
||||
private static Object[][] emptyClasses() {
|
||||
return new Object[][] {
|
||||
new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC },
|
||||
new Object[] { "EmptyHiddenEnum", ACC_ENUM },
|
||||
new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT },
|
||||
new Object[] { "EmptyHiddenInterface", ACC_ABSTRACT|ACC_INTERFACE },
|
||||
new Object[] { "EmptyHiddenAnnotation", ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE },
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if an empty class with valid access flags can be created as a hidden class
|
||||
* as long as it does not violate the restriction of a hidden class.
|
||||
*
|
||||
* A meaningful enum type defines constants of that enum type. So
|
||||
* enum class containing constants of its type should not be a hidden
|
||||
* class.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("emptyClasses")
|
||||
public void emptyHiddenClass(String name, int accessFlags) throws Exception {
|
||||
byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, CD_Enum, accessFlags)
|
||||
: classBytes(name, accessFlags);
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
switch (accessFlags) {
|
||||
case ACC_SYNTHETIC:
|
||||
assertTrue(hc.isSynthetic());
|
||||
assertFalse(hc.isEnum());
|
||||
assertFalse(hc.isAnnotation());
|
||||
assertFalse(hc.isInterface());
|
||||
break;
|
||||
case ACC_ENUM:
|
||||
assertFalse(hc.isSynthetic());
|
||||
assertTrue(hc.isEnum());
|
||||
assertFalse(hc.isAnnotation());
|
||||
assertFalse(hc.isInterface());
|
||||
break;
|
||||
case ACC_ABSTRACT:
|
||||
assertFalse(hc.isSynthetic());
|
||||
assertFalse(hc.isEnum());
|
||||
assertFalse(hc.isAnnotation());
|
||||
assertFalse(hc.isInterface());
|
||||
break;
|
||||
case ACC_ABSTRACT|ACC_INTERFACE:
|
||||
assertFalse(hc.isSynthetic());
|
||||
assertFalse(hc.isEnum());
|
||||
assertFalse(hc.isAnnotation());
|
||||
assertTrue(hc.isInterface());
|
||||
break;
|
||||
case ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE:
|
||||
assertFalse(hc.isSynthetic());
|
||||
assertFalse(hc.isEnum());
|
||||
assertTrue(hc.isAnnotation());
|
||||
assertTrue(hc.isInterface());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unexpected access flag: " + accessFlags);
|
||||
}
|
||||
assertTrue(hc.isHidden());
|
||||
assertEquals(hc.getModifiers(), ACC_PUBLIC | accessFlags);
|
||||
assertFalse(hc.isLocalClass());
|
||||
assertFalse(hc.isMemberClass());
|
||||
assertFalse(hc.isAnonymousClass());
|
||||
assertFalse(hc.isArray());
|
||||
}
|
||||
|
||||
// These class files can't be defined as hidden classes
|
||||
private static Object[][] cantBeHiddenClasses() {
|
||||
return new Object[][] {
|
||||
// a hidden class can't be a field's declaring type
|
||||
// enum class with static final HiddenEnum[] $VALUES:
|
||||
new Object[] { "HiddenEnum" },
|
||||
// supertype of this class is a hidden class
|
||||
new Object[] { "HiddenSuper" },
|
||||
// a record class whose equals(HiddenRecord, Object) method
|
||||
// refers to a hidden class in the parameter type and fails
|
||||
// verification. Perhaps this method signature should be reconsidered.
|
||||
new Object[] { "HiddenRecord" },
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* These class files
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("cantBeHiddenClasses")
|
||||
public void failToDeriveAsHiddenClass(String name) throws Exception {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
|
||||
assertThrows(NoClassDefFoundError.class, () -> lookup().defineHiddenClass(bytes, false).lookupClass());
|
||||
}
|
||||
|
||||
/*
|
||||
* A hidden class can be successfully created but fails to be reflected
|
||||
* if it refers to its own type in the descriptor.
|
||||
* e.g. Class::getMethods resolves the declaring type of fields,
|
||||
* parameter types and return type.
|
||||
*/
|
||||
@Test
|
||||
public void hiddenCantReflect() throws Throwable {
|
||||
HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
|
||||
t.test();
|
||||
|
||||
Class<?> c = t.getClass();
|
||||
Class<?>[] intfs = c.getInterfaces();
|
||||
assertEquals(1, intfs.length);
|
||||
assertSame(HiddenTest.class, intfs[0]);
|
||||
|
||||
var e = assertThrows(NoClassDefFoundError.class, c::getDeclaredMethods);
|
||||
Throwable x = e.getCause();
|
||||
if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cantDefineModule() throws Throwable {
|
||||
Path src = Paths.get("module-info.java");
|
||||
Path dir = CLASSES_DIR.resolve("m");
|
||||
Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8);
|
||||
compileSources(src, dir);
|
||||
|
||||
byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class"));
|
||||
assertThrows(IllegalArgumentException.class, () -> lookup().defineHiddenClass(bytes, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cantDefineClassInAnotherPackage() throws Throwable {
|
||||
Path src = Paths.get("ClassInAnotherPackage.java");
|
||||
Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
|
||||
compileSources(src, CLASSES_DIR);
|
||||
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class"));
|
||||
assertThrows(IllegalArgumentException.class, () -> lookup().defineHiddenClass(bytes, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lessPrivilegedLookup() throws Throwable {
|
||||
Lookup lookup = lookup().dropLookupMode(Lookup.PRIVATE);
|
||||
assertThrows(IllegalAccessException.class, () -> lookup.defineHiddenClass(hiddenClassBytes, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badClassFileVersion() throws Throwable {
|
||||
Path dir = Paths.get(System.getProperty("test.classes", "."));
|
||||
byte[] bytes = Files.readAllBytes(dir.resolve("BadClassFileVersion.class"));
|
||||
assertThrows(UnsupportedClassVersionError.class, () -> lookup().defineHiddenClass(bytes, false));
|
||||
}
|
||||
|
||||
// malformed class files
|
||||
private static Object[][] malformedClassFiles() throws IOException {
|
||||
Path dir = Paths.get(System.getProperty("test.classes", "."));
|
||||
return new Object[][] {
|
||||
// `this_class` has invalid CP entry
|
||||
new Object[] { Files.readAllBytes(dir.resolve("BadClassFile.class")) },
|
||||
new Object[] { Files.readAllBytes(dir.resolve("BadClassFile2.class")) },
|
||||
// truncated file
|
||||
new Object[] { new byte[0] },
|
||||
new Object[] { new byte[] {(byte) 0xCA, (byte) 0xBA, (byte) 0xBE, (byte) 0x00} },
|
||||
};
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("malformedClassFiles")
|
||||
public void badClassFile(byte[] bytes) throws Throwable {
|
||||
assertThrows(ClassFormatError.class, () -> lookup().defineHiddenClass(bytes, false));
|
||||
}
|
||||
|
||||
private static Object[][] nestedTypesOrAnonymousClass() {
|
||||
return new Object[][] {
|
||||
// class file with bad InnerClasses or EnclosingMethod attribute
|
||||
new Object[] { "Outer", null },
|
||||
new Object[] { "Outer$Inner", "Outer" },
|
||||
new Object[] { "EnclosingClass", null },
|
||||
new Object[] { "EnclosingClass$1", "EnclosingClass" },
|
||||
};
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nestedTypesOrAnonymousClass")
|
||||
public void hasInnerClassesOrEnclosingMethodAttribute(String className, String badDeclaringClassName) throws Throwable {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve(className + ".class"));
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
hiddenClassWithBadAttribute(hc, badDeclaringClassName);
|
||||
}
|
||||
|
||||
// define a hidden class with static nest membership
|
||||
@Test
|
||||
public void hasStaticNestHost() throws Exception {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
hiddenClassWithBadAttribute(hc, "Outer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasStaticNestMembers() throws Throwable {
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class"));
|
||||
Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
|
||||
assertHiddenClass(hc);
|
||||
assertSame(hc, hc.getNestHost());
|
||||
Class<?>[] members = hc.getNestMembers();
|
||||
assertEquals(1, members.length);
|
||||
assertSame(hc, members[0]);
|
||||
}
|
||||
|
||||
// a hidden class with bad InnerClasses or EnclosingMethod attribute
|
||||
private void hiddenClassWithBadAttribute(Class<?> hc, String badDeclaringClassName) {
|
||||
assertTrue(hc.isHidden());
|
||||
assertNull(hc.getCanonicalName());
|
||||
assertTrue(hc.getName().contains("/"));
|
||||
|
||||
if (badDeclaringClassName == null) {
|
||||
// the following reflection API assumes a good name in InnerClasses
|
||||
// or EnclosingMethod attribute can successfully be resolved.
|
||||
assertFalse(hc.getSimpleName().isEmpty());
|
||||
assertFalse(hc.isAnonymousClass());
|
||||
assertFalse(hc.isLocalClass());
|
||||
assertFalse(hc.isMemberClass());
|
||||
} else {
|
||||
declaringClassNotFound(hc, badDeclaringClassName);
|
||||
}
|
||||
|
||||
// validation of nest membership
|
||||
assertSame(hc, hc.getNestHost());
|
||||
// validate the static nest membership
|
||||
Class<?>[] members = hc.getNestMembers();
|
||||
assertEquals(1, members.length);
|
||||
assertSame(hc, members[0]);
|
||||
}
|
||||
|
||||
// Class::getSimpleName, Class::isMemberClass
|
||||
private void declaringClassNotFound(Class<?> c, String cn) {
|
||||
var e = assertThrows(NoClassDefFoundError.class, c::isMemberClass);
|
||||
if (!e.getMessage().equals(cn)) {
|
||||
throw e;
|
||||
}
|
||||
e = assertThrows(NoClassDefFoundError.class, c::getSimpleName);
|
||||
if (!e.getMessage().equals(cn)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void singletonNest(Class<?> hc) {
|
||||
assertSame(hc, hc.getNestHost());
|
||||
assertEquals(1, hc.getNestMembers().length);
|
||||
assertSame(hc, hc.getNestMembers()[0]);
|
||||
}
|
||||
|
||||
private static void assertHiddenClass(Class<?> hc) {
|
||||
assertTrue(hc.isHidden());
|
||||
assertNull(hc.getCanonicalName());
|
||||
assertTrue(hc.getName().contains("/"));
|
||||
assertFalse(hc.isAnonymousClass());
|
||||
assertFalse(hc.isLocalClass());
|
||||
assertFalse(hc.isMemberClass());
|
||||
assertFalse(hc.getSimpleName().isEmpty()); // sanity check
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname, int accessFlags) {
|
||||
return classBytes(classname, CD_Object, accessFlags);
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname, ClassDesc superType, int accessFlags) {
|
||||
return ClassFile.of().build(ClassDesc.ofInternalName(classname), clb -> clb
|
||||
.withVersion(JAVA_14_VERSION, 0)
|
||||
.withFlags(accessFlags | ACC_PUBLIC)
|
||||
.withSuperclass(superType));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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
|
||||
* @library /test/lib
|
||||
* @build HiddenNestmateTest
|
||||
* @run junit/othervm HiddenNestmateTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class HiddenNestmateTest {
|
||||
private static final ClassDesc CD_HiddenNestmateTest = HiddenNestmateTest.class.describeConstable().orElseThrow();
|
||||
private static final byte[] bytes = classBytes("HiddenInjected");
|
||||
|
||||
private static void assertNestmate(Lookup lookup) {
|
||||
assertTrue((lookup.lookupModes() & PRIVATE) != 0);
|
||||
assertTrue((lookup.lookupModes() & MODULE) != 0);
|
||||
|
||||
Class<?> hiddenClass = lookup.lookupClass();
|
||||
Class<?> nestHost = hiddenClass.getNestHost();
|
||||
assertTrue(hiddenClass.isHidden());
|
||||
assertSame(MethodHandles.lookup().lookupClass(), nestHost);
|
||||
|
||||
// hidden nestmate is not listed in the return array of getNestMembers
|
||||
assertTrue(Stream.of(nestHost.getNestMembers()).noneMatch(k -> k == hiddenClass));
|
||||
assertTrue(hiddenClass.isNestmateOf(lookup.lookupClass()));
|
||||
assertArrayEquals(nestHost.getNestMembers(), hiddenClass.getNestMembers());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a hidden class to have no access to private members of another class
|
||||
*/
|
||||
@Test
|
||||
public void hiddenClass() throws Throwable {
|
||||
// define a hidden class
|
||||
Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
|
||||
Class<?> c = lookup.lookupClass();
|
||||
assertTrue(lookup.hasFullPrivilegeAccess());
|
||||
assertEquals(ORIGINAL, lookup.lookupModes() & ORIGINAL);
|
||||
assertSame(c, c.getNestHost()); // host of its own nest
|
||||
assertTrue(c.isHidden());
|
||||
|
||||
// invoke int test(HiddenNestmateTest o) via MethodHandle
|
||||
MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
|
||||
MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
|
||||
assertThrows(IllegalAccessError.class, () -> {
|
||||
int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this);
|
||||
});
|
||||
|
||||
// invoke int test(HiddenNestmateTest o)
|
||||
assertThrows(IllegalAccessError.class, () -> testInjectedClass(c));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a hidden class to have access to private members of its nestmates
|
||||
*/
|
||||
@Test
|
||||
public void hiddenNestmate() throws Throwable {
|
||||
// define a hidden nestmate class
|
||||
Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
|
||||
Class<?> c = lookup.lookupClass();
|
||||
assertNestmate(lookup);
|
||||
|
||||
// invoke int test(HiddenNestmateTest o) via MethodHandle
|
||||
MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
|
||||
MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
|
||||
int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this);
|
||||
assertEquals(privMethod(), x);
|
||||
|
||||
// invoke int test(HiddenNestmateTest o)
|
||||
int x1 = testInjectedClass(c);
|
||||
assertEquals(privMethod(), x1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a hidden class created with NESTMATE and STRONG option is a nestmate
|
||||
*/
|
||||
@Test
|
||||
public void hiddenStrongClass() throws Throwable {
|
||||
// define a hidden class strongly referenced by the class loader
|
||||
Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, STRONG);
|
||||
assertNestmate(lookup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail to create a hidden class if dropping PRIVATE lookup mode
|
||||
*/
|
||||
@Test
|
||||
public void noPrivateLookupAccess() throws Throwable {
|
||||
Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
|
||||
assertThrows(IllegalAccessException.class, () -> lookup.defineHiddenClass(bytes, false, NESTMATE));
|
||||
}
|
||||
|
||||
public void teleportToNestmate() throws Throwable {
|
||||
Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
|
||||
assertNestmate(lookup);
|
||||
|
||||
// Teleport to a hidden nestmate
|
||||
Lookup lc = MethodHandles.lookup().in(lookup.lookupClass());
|
||||
assertNotEquals(0, lc.lookupModes() & PRIVATE);
|
||||
assertEquals(0, lc.lookupModes() & ORIGINAL);
|
||||
|
||||
Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
|
||||
assertNestmate(lc2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fail to create a hidden class in a different package from the lookup class' package
|
||||
*/
|
||||
@Test
|
||||
public void notSamePackage() throws Throwable {
|
||||
assertThrows(IllegalArgumentException.class, () -> MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE));
|
||||
}
|
||||
|
||||
/*
|
||||
* invoke int test(HiddenNestmateTest o) method defined in the injected class
|
||||
*/
|
||||
private int testInjectedClass(Class<?> c) throws Throwable {
|
||||
try {
|
||||
Method m = c.getMethod("test", HiddenNestmateTest.class);
|
||||
return (int) m.invoke(c.newInstance(), this);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] classBytes(String classname) {
|
||||
return ClassFile.of().build(ClassDesc.ofInternalName(classname), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.FINAL);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody("test", MethodTypeDesc.of(CD_int, CD_HiddenNestmateTest), PUBLIC, cob -> {
|
||||
cob.aload(1);
|
||||
cob.invokevirtual(CD_HiddenNestmateTest, "privMethod", MethodTypeDesc.of(CD_int));
|
||||
cob.ireturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private int privMethod() { return 1234; }
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2025, 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
|
||||
* @summary define a lambda proxy class whose target class has an invalid
|
||||
* nest membership
|
||||
* @run junit/othervm p.LambdaNestedInnerTest
|
||||
*/
|
||||
|
||||
package p;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class LambdaNestedInnerTest {
|
||||
private static final String INNER_CLASSNAME = "p.LambdaNestedInnerTest$Inner";
|
||||
private static final String DIR = "missingOuter";
|
||||
public static class Inner implements Runnable {
|
||||
// generate lambda proxy class
|
||||
private Runnable lambda1 = this::doit;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// validate the lambda proxy class
|
||||
Class<?> lambdaProxyClass = lambda1.getClass();
|
||||
assertTrue(lambdaProxyClass.isHidden());
|
||||
System.out.format("%s nest host %s nestmate of Inner class %s%n",
|
||||
lambdaProxyClass, lambdaProxyClass.getNestHost(),
|
||||
lambdaProxyClass.isNestmateOf(Inner.class));
|
||||
assertTrue(lambdaProxyClass.getNestHost() == Inner.class.getNestHost());
|
||||
assertTrue(Arrays.equals(lambdaProxyClass.getNestMembers(), Inner.class.getNestMembers()));
|
||||
assertTrue(lambdaProxyClass.isNestmateOf(Inner.class));
|
||||
lambda1.run();
|
||||
}
|
||||
|
||||
// junit may not be visible to this class
|
||||
private static void assertTrue(boolean x) {
|
||||
if (!x) {
|
||||
throw new AssertionError("expected true but found false");
|
||||
}
|
||||
}
|
||||
|
||||
private void doit() {
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
String filename = INNER_CLASSNAME.replace('.', File.separatorChar) + ".class";
|
||||
Path src = Paths.get(System.getProperty("test.classes"), filename);
|
||||
Path dest = Paths.get(DIR, filename);
|
||||
Files.createDirectories(dest.getParent());
|
||||
Files.copy(src, dest, REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
Class<?> inner = Class.forName(INNER_CLASSNAME);
|
||||
// inner class is a nest member of LambdaNestedInnerTest
|
||||
Class<?> nestHost = inner.getNestHost();
|
||||
assertSame(LambdaNestedInnerTest.class, nestHost);
|
||||
Set<Class<?>> members = Arrays.stream(nestHost.getNestMembers()).collect(Collectors.toSet());
|
||||
assertEquals(Set.of(nestHost, inner, TestLoader.class), members);
|
||||
|
||||
// spin lambda proxy hidden class
|
||||
Runnable runnable = (Runnable) inner.newInstance();
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestHostNotExist() throws Exception {
|
||||
URL[] urls = new URL[] { Paths.get(DIR).toUri().toURL() };
|
||||
URLClassLoader loader = new URLClassLoader(urls, null);
|
||||
Class<?> inner = loader.loadClass(INNER_CLASSNAME);
|
||||
assertSame(loader, inner.getClassLoader());
|
||||
assertSame(inner, inner.getNestHost()); // linkage error ignored
|
||||
|
||||
Runnable runnable = (Runnable) inner.newInstance();
|
||||
// this validates the lambda proxy class
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests IncompatibleClassChangeError thrown since the true nest host is not
|
||||
* in the same runtime package as the hidden class
|
||||
*/
|
||||
@Test
|
||||
public void nestHostNotSamePackage() throws Exception {
|
||||
URL[] urls = new URL[] { Paths.get(DIR).toUri().toURL() };
|
||||
TestLoader loader = new TestLoader(urls);
|
||||
|
||||
Class<?> inner = loader.loadClass(INNER_CLASSNAME);
|
||||
assertSame(loader, inner.getClassLoader());
|
||||
assertSame(inner, inner.getNestHost()); // linkage error ignored.
|
||||
|
||||
Runnable runnable = (Runnable) inner.newInstance();
|
||||
// this validates the lambda proxy class
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
static class TestLoader extends URLClassLoader {
|
||||
TestLoader(URL[] urls) {
|
||||
super(urls, TestLoader.class.getClassLoader());
|
||||
}
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
if (INNER_CLASSNAME.equals(name)) {
|
||||
return findClass(name);
|
||||
} else {
|
||||
// delegate to its parent
|
||||
return loadClass(name, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2026, 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 8245432
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib
|
||||
* @requires !java.enablePreview
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* @run junit PreviewHiddenClass
|
||||
* @summary verify UnsupportedClassVersionError thrown when defining a hidden class
|
||||
* with preview minor version but --enable-preview is not set
|
||||
* @comment This test itself cannot enablePreview, or hidden class definition
|
||||
* will pass
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class PreviewHiddenClass {
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
|
||||
private static final Path CLASSES_DIR = Paths.get("classes");
|
||||
|
||||
@Test
|
||||
public void previewNotEnabled() throws Exception {
|
||||
// compile a class with --enable-preview
|
||||
Path sourceFile = SRC_DIR.resolve("HiddenInterface.java");
|
||||
String[] options = new String[] {
|
||||
"--enable-preview", "-source", String.valueOf(Runtime.version().feature()), "-XDforcePreview" };
|
||||
if (!CompilerUtils.compile(sourceFile, CLASSES_DIR, options)) {
|
||||
throw new RuntimeException("Compilation of the test failed: " + sourceFile);
|
||||
}
|
||||
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenInterface.class"));
|
||||
var dis = new DataInputStream(new ByteArrayInputStream(bytes));
|
||||
dis.skipBytes(4); // 0xCAFEBABE
|
||||
assertEquals(65535, dis.readUnsignedShort()); // Minor version
|
||||
assertThrows(UnsupportedClassVersionError.class, () -> MethodHandles.lookup().defineHiddenClass(bytes, false));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile for this class will be used to define a hidden class
|
||||
* The load of this class will fail because hidden classes cannot
|
||||
* use their name in field signatures.
|
||||
*/
|
||||
public class SelfRefField implements Test {
|
||||
|
||||
SelfRefField other = null;
|
||||
|
||||
private void realTest() {
|
||||
other = this; // field signature test
|
||||
}
|
||||
|
||||
public void test() {
|
||||
realTest();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile for this class will be used to define a hidden class
|
||||
* The load of this class will fail because hidden classes cannot
|
||||
* use their names in method signatures.
|
||||
*/
|
||||
public class SelfRefMethod implements Test {
|
||||
|
||||
private void realTest() {
|
||||
SelfRefMethod local = this;
|
||||
set_other(local); // method signature test
|
||||
}
|
||||
|
||||
private void set_other(SelfRefMethod t) {
|
||||
}
|
||||
|
||||
public void test() {
|
||||
realTest();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 2020, 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
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* SelfReferenceDescriptor
|
||||
* @run main/othervm -Xverify:remote SelfReferenceDescriptor
|
||||
* @summary Test that a hidden class cannot be referenced in descriptor
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
/* package-private */ interface Test {
|
||||
void test();
|
||||
}
|
||||
|
||||
public class SelfReferenceDescriptor {
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC);
|
||||
private static final Path CLASSES_DIR = Paths.get("classes");
|
||||
|
||||
static void compileSources(Path sourceFile, String... options) throws IOException {
|
||||
Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
|
||||
if (options != null && options.length > 0) {
|
||||
ops = Stream.concat(ops, Arrays.stream(options));
|
||||
}
|
||||
if (!CompilerUtils.compile(sourceFile, CLASSES_DIR, ops.toArray(String[]::new))) {
|
||||
throw new RuntimeException("Compilation of the test failed: " + sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a hidden class cannot use its own name in a field
|
||||
// signature.
|
||||
public static void hiddenClassInFieldDescriptor() throws Exception {
|
||||
compileSources(SRC_DIR.resolve("SelfRefField.java"));
|
||||
Path path = CLASSES_DIR.resolve("SelfRefField.class");
|
||||
byte[] bytes = Files.readAllBytes(path);
|
||||
try {
|
||||
lookup().defineHiddenClass(bytes, false, NESTMATE);
|
||||
throw new RuntimeException("expected NCDFE in defining SelfRefField hidden class");
|
||||
} catch (NoClassDefFoundError e) {
|
||||
if (!e.getMessage().contains("SelfRefField")) throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a hidden class cannot use its own name in a method
|
||||
// signature.
|
||||
public static void hiddenClassInMethodDescriptor() throws Exception {
|
||||
compileSources(SRC_DIR.resolve("SelfRefMethod.java"));
|
||||
Path path = CLASSES_DIR.resolve("SelfRefMethod.class");
|
||||
byte[] bytes = Files.readAllBytes(path);
|
||||
try {
|
||||
lookup().defineHiddenClass(bytes, false, NESTMATE);
|
||||
throw new RuntimeException("expected NCDFE in defining SelfRefMethod hidden class");
|
||||
} catch (NoClassDefFoundError e) {
|
||||
if (!e.getMessage().contains("SelfRefMethod")) throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
hiddenClassInMethodDescriptor();
|
||||
hiddenClassInFieldDescriptor();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2025, 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 8266925
|
||||
* @summary hidden class members can't be statically invocable
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @build java.base/*
|
||||
* @run junit StaticInvocableTest
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.LookupHelper;
|
||||
import java.lang.reflect.AccessFlag;
|
||||
|
||||
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
|
||||
import static java.lang.classfile.ClassFile.ACC_STATIC;
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
import static java.lang.constant.ConstantDescs.CD_int;
|
||||
import static java.lang.constant.ConstantDescs.INIT_NAME;
|
||||
import static java.lang.constant.ConstantDescs.MTD_void;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class StaticInvocableTest {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
StaticInvocableTest test = new StaticInvocableTest();
|
||||
test.testJavaLang();
|
||||
test.testJavaUtil();
|
||||
test.testJdkInternalMisc();
|
||||
test.testJavaLangInvoke();
|
||||
test.testProhibitedJavaPkg();
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
|
||||
// Test hidden classes from different packages
|
||||
// (see j.l.i.InvokerBytecodeGenerator::isStaticallyInvocable).
|
||||
@Test public void testJavaLang() throws Throwable { test("java/lang"); }
|
||||
@Test public void testJavaUtil() throws Throwable { test("java/util"); }
|
||||
@Test public void testJdkInternalMisc() throws Throwable { test("jdk/internal/misc"); }
|
||||
@Test public void testJavaLangInvoke() throws Throwable { test("java/lang/invoke"); }
|
||||
@Test public void testProhibitedJavaPkg() throws Throwable {
|
||||
try {
|
||||
test("java/prohibited");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Expected SecurityException");
|
||||
}
|
||||
|
||||
private static void test(String pkg) throws Throwable {
|
||||
byte[] bytes = dumpClass(pkg);
|
||||
Lookup lookup;
|
||||
if (pkg.equals("java/prohibited")) {
|
||||
StaticInvocableTest sampleclass = new StaticInvocableTest();
|
||||
lookup = LookupHelper.newLookup(sampleclass.getClass());
|
||||
} else if (pkg.equals("java/lang")) {
|
||||
lookup = LookupHelper.newLookup(Object.class);
|
||||
} else if (pkg.equals("java/util")) {
|
||||
lookup = LookupHelper.newLookup(java.util.ArrayList.class);
|
||||
} else if (pkg.equals("jdk/internal/misc")) {
|
||||
lookup = LookupHelper.newLookup(jdk.internal.misc.Signal.class);
|
||||
} else if (pkg.equals("java/lang/invoke")) {
|
||||
lookup = LookupHelper.newLookup(java.lang.invoke.CallSite.class);
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected pkg: " + pkg);
|
||||
}
|
||||
|
||||
// Define hidden class
|
||||
Lookup l = lookup.defineHiddenClass(bytes, true);
|
||||
|
||||
MethodType t = MethodType.methodType(Object.class, int.class);
|
||||
MethodHandle target = l.findStatic(l.lookupClass(), "get", t);
|
||||
|
||||
// Wrap target into LF (convert) to get "target" referenced from LF
|
||||
MethodHandle wrappedMH = target.asType(MethodType.methodType(Object.class, Integer.class));
|
||||
|
||||
// Invoke enough times to provoke LF compilation to bytecode.
|
||||
for (int i = 0; i<100; i++) {
|
||||
Object r = wrappedMH.invokeExact((Integer)1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructs bytecode for the following class:
|
||||
* public class pkg.MyClass {
|
||||
* MyClass() {}
|
||||
* public Object get(int i) { return null; }
|
||||
* }
|
||||
*/
|
||||
public static byte[] dumpClass(String pkg) {
|
||||
return ClassFile.of().build(ClassDesc.of(pkg.replace('/', '.'), "MyClass"), clb -> {
|
||||
clb.withSuperclass(CD_Object);
|
||||
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
|
||||
clb.withMethodBody(INIT_NAME, MTD_void, 0, cob -> {
|
||||
cob.aload(0);
|
||||
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
|
||||
cob.return_();
|
||||
});
|
||||
clb.withMethodBody("get", MethodTypeDesc.of(CD_Object, CD_int),
|
||||
ACC_PUBLIC | ACC_STATIC, cob -> {
|
||||
cob.aconst_null();
|
||||
cob.areturn();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, 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 8242013
|
||||
* @run junit/othervm test.TypeDescriptorTest
|
||||
* @summary Test TypeDescriptor::descriptorString for hidden classes which
|
||||
* cannot be used to produce ConstantDesc via ClassDesc or
|
||||
* MethodTypeDesc factory methods
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import static java.lang.invoke.MethodType.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class TypeDescriptorTest {
|
||||
private static final Lookup HC_LOOKUP = defineHiddenClass();
|
||||
private static final Class<?> HC = HC_LOOKUP.lookupClass();
|
||||
private static Lookup defineHiddenClass() {
|
||||
String classes = System.getProperty("test.classes");
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(Paths.get(classes, "test/HiddenClass.class"));
|
||||
return MethodHandles.lookup().defineHiddenClass(bytes, true);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object[][] constables() throws Exception {
|
||||
Class<?> hcArray = Array.newInstance(HC, 1).getClass();
|
||||
return new Object[][] {
|
||||
new Object[] { HC },
|
||||
new Object[] { hcArray },
|
||||
new Object[] { methodType(HC) },
|
||||
new Object[] { methodType(void.class, HC) },
|
||||
new Object[] { methodType(void.class, HC, int.class) },
|
||||
new Object[] { HC_LOOKUP.findStatic(HC, "m", methodType(void.class)) },
|
||||
new Object[] { HC_LOOKUP.findStaticVarHandle(HC, "f", Object.class) }
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Hidden classes have no nominal descriptor.
|
||||
* Constable::describeConstable returns empty optional.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("constables")
|
||||
public void noNominalDescriptor(Constable constable) {
|
||||
assertTrue(constable.describeConstable().isEmpty());
|
||||
}
|
||||
|
||||
/*
|
||||
* ClassDesc factory methods throws IAE with the name or descriptor string
|
||||
* from a hidden class
|
||||
*/
|
||||
@Test
|
||||
public void testClassDesc() {
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofDescriptor(HC.descriptorString()));
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofDescriptor(HC.getName()));
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassDesc.of(HC.getPackageName(), HC.getSimpleName()));
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassDesc.of(HC.getName()));
|
||||
}
|
||||
|
||||
private static Object[][] typeDescriptors() throws Exception {
|
||||
Class<?> hcArray = Array.newInstance(HC, 1, 1).getClass();
|
||||
return new Object[][] {
|
||||
new Object[] { HC, "Ltest/HiddenClass.0x[0-9a-f]+;"},
|
||||
new Object[] { hcArray, "\\[\\[Ltest/HiddenClass.0x[0-9a-f]+;"},
|
||||
new Object[] { methodType(HC), "\\(\\)Ltest/HiddenClass.0x[0-9a-f]+;" },
|
||||
new Object[] { methodType(void.class, HC), "\\(Ltest/HiddenClass.0x[0-9a-f]+;\\)V" },
|
||||
new Object[] { methodType(void.class, HC, int.class, Object.class), "\\(Ltest/HiddenClass.0x[0-9a-f]+;ILjava/lang/Object;\\)V" }
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Hidden classes have no nominal type descriptor
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("typeDescriptors")
|
||||
public void testTypeDescriptor(TypeDescriptor td, String regex) throws Exception {
|
||||
String desc = td.descriptorString();
|
||||
assertTrue(desc.matches(regex));
|
||||
|
||||
if (td instanceof Class) {
|
||||
assertThrows(IllegalArgumentException.class, () -> ClassDesc.ofDescriptor(desc));
|
||||
} else if (td instanceof MethodType) {
|
||||
assertThrows(IllegalArgumentException.class, () -> MethodTypeDesc.ofDescriptor(desc));
|
||||
}
|
||||
}
|
||||
|
||||
private static Object[][] methodTypes() throws Exception {
|
||||
Class<?> hcArray = Array.newInstance(HC, 1, 1).getClass();
|
||||
return new Object[][] {
|
||||
new Object[] { methodType(HC), "\\(\\)Ltest/HiddenClass.0x[0-9a-f]+;" },
|
||||
new Object[] { methodType(void.class, hcArray), "\\(\\[\\[Ltest/HiddenClass.0x[0-9a-f]+;\\)V" },
|
||||
new Object[] { methodType(void.class, int.class, HC), "\\(ILtest/HiddenClass.0x[0-9a-f]+;\\)V" }
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Test MethodType::toMethodDescriptorString with MethodType referencing to hidden class
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("methodTypes")
|
||||
public void testToMethodDescriptorString(MethodType mtype, String regex) throws Exception {
|
||||
String desc = mtype.toMethodDescriptorString();
|
||||
assertTrue(desc.matches(regex));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> MethodType.fromMethodDescriptorString(desc, TypeDescriptorTest.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
class HiddenClass {
|
||||
private static final Object f = new Object();
|
||||
public static void m() {
|
||||
}
|
||||
}
|
||||
241
test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java
Normal file
241
test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2025, 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
|
||||
* @summary verify if the hidden class is unloaded when the class loader is GC'ed
|
||||
* @modules jdk.compiler
|
||||
* @library /test/lib/
|
||||
* @build jdk.test.lib.util.ForceGC
|
||||
* @run junit/othervm UnloadingTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import jdk.test.lib.util.ForceGC;
|
||||
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class UnloadingTest {
|
||||
private static final Path CLASSES_DIR = Paths.get("classes");
|
||||
private static byte[] hiddenClassBytes;
|
||||
|
||||
@BeforeAll
|
||||
static void setup() throws IOException {
|
||||
Path src = Paths.get(Utils.TEST_SRC, "src", "LookupHelper.java");
|
||||
if (!CompilerUtils.compile(src, CLASSES_DIR)) {
|
||||
throw new RuntimeException("Compilation of the test failed: " + src);
|
||||
}
|
||||
|
||||
hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("LookupHelper.class"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a hidden class is unloaded while the loader remains strongly reachable
|
||||
*/
|
||||
@Test
|
||||
public void unloadable() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> helper = Class.forName("LookupHelper", true, loader);
|
||||
Method m = helper.getMethod("getLookup");
|
||||
Lookup lookup = (Lookup)m.invoke(null);
|
||||
HiddenClassUnloader unloader = createHiddenClass(lookup, false);
|
||||
// the hidden class should be unloaded
|
||||
unloader.unload();
|
||||
|
||||
// loader is strongly reachable
|
||||
Reference.reachabilityFence(loader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that a hidden class is not unloaded when the loader is strongly reachable
|
||||
*/
|
||||
@Test
|
||||
public void notUnloadable() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> helper = Class.forName("LookupHelper", true, loader);
|
||||
Method m = helper.getMethod("getLookup");
|
||||
Lookup lookup = (Lookup)m.invoke(null);
|
||||
HiddenClassUnloader unloader = createHiddenClass(lookup, true);
|
||||
assertFalse(unloader.tryUnload()); // hidden class is not unloaded
|
||||
|
||||
// loader is strongly reachable
|
||||
Reference.reachabilityFence(loader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a nest of two hidden classes.
|
||||
* They can be unloaded even the loader is strongly reachable
|
||||
*/
|
||||
@Test
|
||||
public void hiddenClassNest() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> helper = Class.forName("LookupHelper", true, loader);
|
||||
Method m = helper.getMethod("getLookup");
|
||||
Lookup lookup = (Lookup)m.invoke(null);
|
||||
HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, false);
|
||||
|
||||
// keep a strong reference to the nest member class
|
||||
Class<?> member = unloaders[1].weakRef.get();
|
||||
assertNotNull(member);
|
||||
// nest host and member will not be unloaded
|
||||
assertFalse(unloaders[0].tryUnload());
|
||||
assertFalse(unloaders[1].tryUnload());
|
||||
|
||||
// clear the reference to the nest member
|
||||
Reference.reachabilityFence(member);
|
||||
member = null;
|
||||
|
||||
// nest host and member will be unloaded
|
||||
unloaders[0].unload();
|
||||
unloaders[1].unload();
|
||||
|
||||
// loader is strongly reachable
|
||||
Reference.reachabilityFence(loader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a nest with a hidden class nest host and strong nest member.
|
||||
* Test that both are not unloaded
|
||||
*/
|
||||
@Test
|
||||
public void hiddenClassNestStrongMember() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> helper = Class.forName("LookupHelper", true, loader);
|
||||
Method m = helper.getMethod("getLookup");
|
||||
Lookup lookup = (Lookup)m.invoke(null);
|
||||
HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, true);
|
||||
assertFalse(unloaders[0].tryUnload()); // nest host cannot be unloaded
|
||||
assertFalse(unloaders[1].tryUnload()); // nest member cannot be unloaded
|
||||
|
||||
// loader is strongly reachable
|
||||
Reference.reachabilityFence(loader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a nest with a strong hidden nest host and a hidden class member.
|
||||
* The nest member can be unloaded whereas the nest host will not be unloaded.
|
||||
*/
|
||||
@Test
|
||||
public void hiddenClassNestStrongHost() throws Exception {
|
||||
TestLoader loader = new TestLoader();
|
||||
Class<?> helper = Class.forName("LookupHelper", true, loader);
|
||||
Method m = helper.getMethod("getLookup");
|
||||
Lookup lookup = (Lookup)m.invoke(null);
|
||||
HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, true, false);
|
||||
assertFalse(unloaders[0].tryUnload()); // nest host cannot be unloaded
|
||||
unloaders[1].unload();
|
||||
|
||||
// loader is strongly reachable
|
||||
Reference.reachabilityFence(loader);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a HiddenClassUnloader that holds a weak reference to the newly created
|
||||
* hidden class.
|
||||
*/
|
||||
static HiddenClassUnloader createHiddenClass(Lookup lookup, boolean strong) throws Exception {
|
||||
Class<?> hc;
|
||||
if (strong) {
|
||||
hc = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG).lookupClass();
|
||||
} else {
|
||||
hc = lookup.defineHiddenClass(hiddenClassBytes, false).lookupClass();
|
||||
}
|
||||
assertSame(lookup.lookupClass().getClassLoader(), hc.getClassLoader());
|
||||
return new HiddenClassUnloader(hc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an array of HiddenClassUnloader with two elements: the first element
|
||||
* is for the nest host and the second element is for the nest member.
|
||||
*/
|
||||
static HiddenClassUnloader[] createNestOfTwoHiddenClasses(Lookup lookup, boolean strongHost, boolean strongMember) throws Exception {
|
||||
Lookup hostLookup;
|
||||
if (strongHost) {
|
||||
hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG);
|
||||
} else {
|
||||
hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false);
|
||||
}
|
||||
Class<?> host = hostLookup.lookupClass();
|
||||
Class<?> member;
|
||||
if (strongMember) {
|
||||
member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE, STRONG).lookupClass();
|
||||
} else {
|
||||
member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE).lookupClass();
|
||||
}
|
||||
assertSame(host, member.getNestHost());
|
||||
return new HiddenClassUnloader[] { new HiddenClassUnloader(host), new HiddenClassUnloader(member) };
|
||||
}
|
||||
|
||||
static class HiddenClassUnloader {
|
||||
private final WeakReference<Class<?>> weakRef;
|
||||
private HiddenClassUnloader(Class<?> hc) {
|
||||
assertTrue(hc.isHidden());
|
||||
this.weakRef = new WeakReference<>(hc);
|
||||
}
|
||||
|
||||
void unload() {
|
||||
// Force garbage collection to trigger unloading of class loader
|
||||
// and native library.
|
||||
if (!ForceGC.wait(() -> weakRef.refersTo(null))) {
|
||||
throw new RuntimeException("loader " + " not unloaded!");
|
||||
}
|
||||
}
|
||||
|
||||
boolean tryUnload() {
|
||||
return ForceGC.waitFor(() -> weakRef.refersTo(null), 2000L);
|
||||
}
|
||||
}
|
||||
|
||||
static class TestLoader extends URLClassLoader {
|
||||
static URL[] toURLs() {
|
||||
try {
|
||||
return new URL[] { CLASSES_DIR.toUri().toURL() };
|
||||
} catch (MalformedURLException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static AtomicInteger counter = new AtomicInteger();
|
||||
TestLoader() {
|
||||
super("testloader-" + counter.addAndGet(1), toURLs(), ClassLoader.getSystemClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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 java.lang.invoke;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
public class LookupHelper {
|
||||
public static Lookup newLookup(Class<?> c) {
|
||||
return new Lookup(c);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
public abstract class AbstractClass {
|
||||
abstract void test();
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
public class EnclosingClass {
|
||||
public static void run() {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface HiddenAnnotation {
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile does not fail verification but would fail when
|
||||
* getMethod
|
||||
*/
|
||||
public class HiddenCantReflect implements HiddenTest {
|
||||
|
||||
HiddenCantReflect other = null;
|
||||
|
||||
private String realTest() {
|
||||
Object o = other;
|
||||
HiddenCantReflect local = this;
|
||||
local = other;
|
||||
local = (HiddenCantReflect) o;
|
||||
local = new HiddenCantReflect();
|
||||
|
||||
set_other(null);
|
||||
|
||||
local = getThis();
|
||||
|
||||
set_other_maybe(new Object());
|
||||
set_other_maybe(this);
|
||||
return "HiddenCantReflect";
|
||||
}
|
||||
|
||||
private HiddenCantReflect getThis() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void set_other(HiddenCantReflect t) {
|
||||
other = t;
|
||||
}
|
||||
|
||||
private void set_other_maybe(Object o) {
|
||||
if (o instanceof HiddenCantReflect) {
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
String result = realTest();
|
||||
// Make sure that the Utf8 constant pool entry for "HiddenCantReflect" is okay.
|
||||
if (!result.substring(0, 7).equals("HiddenC") ||
|
||||
!result.substring(7).equals("antReflect")) {
|
||||
throw new RuntimeException("'HiddenCantReflect string is bad: " + result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile for this class will be loaded directly and used to define
|
||||
* a hidden class.
|
||||
*/
|
||||
public class HiddenClass implements HiddenTest {
|
||||
|
||||
HiddenClass other = null;
|
||||
|
||||
private String realTest() {
|
||||
Object o = other;
|
||||
HiddenClass local = this;
|
||||
local = other;
|
||||
local = (HiddenClass) o;
|
||||
local = new HiddenClass();
|
||||
|
||||
set_other_maybe(new Object());
|
||||
set_other_maybe(this);
|
||||
return "HiddenClass";
|
||||
}
|
||||
|
||||
private void set_other_maybe(Object o) {
|
||||
if (o instanceof HiddenClass) {
|
||||
}
|
||||
}
|
||||
|
||||
public void test() {
|
||||
String result = realTest();
|
||||
// Make sure that the Utf8 constant pool entry for "HiddenClass" is okay.
|
||||
if (!result.substring(0, 7).equals("HiddenC") ||
|
||||
!result.substring(7).equals("lass")) {
|
||||
throw new RuntimeException("'HiddenClass string is bad: " + result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile for this class will be loaded directly and used to define
|
||||
* a hidden class.
|
||||
*/
|
||||
public class HiddenClassThrow implements HiddenTest {
|
||||
|
||||
public void test() {
|
||||
throw new RuntimeException(this.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// fail to create HiddenEnum as a hidden class as it fails verification
|
||||
// <clinit> initializes the static final HiddenEnum[] $VALUES field
|
||||
public enum HiddenEnum {
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The classfile for this class will be used to define a hidden interface.
|
||||
* This class will fail to be created as a hidden class because hidden classes
|
||||
* cannot user their name in field signatures.
|
||||
*/
|
||||
public interface HiddenInterface {
|
||||
default void test() {
|
||||
record R() {}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class HiddenLambda implements HiddenTest {
|
||||
public void test() {
|
||||
Function<Object, String> f = o -> o.toString();
|
||||
String s = f.apply(this);
|
||||
throw new Error("thrown by " + s);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// fail to create HiddenRecord as a hidden class
|
||||
// verification fails in the BSM to invoke equals(HiddenRecord, Object) method
|
||||
record HiddenRecord(int i) { }
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
public class HiddenSuper extends HiddenClass implements HiddenTest {
|
||||
private void realTest() {
|
||||
}
|
||||
|
||||
public void test() {
|
||||
realTest();
|
||||
}
|
||||
}
|
||||
36
test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java
Normal file
36
test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Lambda implements HiddenTest {
|
||||
public void test() {
|
||||
Function<Object, String> f = Object::toString;
|
||||
String s = f.apply(this);
|
||||
throw new Error("thrown by " + s);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getClass().getName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
public class LookupHelper {
|
||||
private Lookup lookup() {
|
||||
return MethodHandles.lookup();
|
||||
}
|
||||
public static Lookup getLookup() {
|
||||
return (new LookupHelper()).lookup();
|
||||
}
|
||||
}
|
||||
27
test/jdk/java/lang/invoke/defineHiddenClass/src/Outer.java
Normal file
27
test/jdk/java/lang/invoke/defineHiddenClass/src/Outer.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2019, 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.
|
||||
*/
|
||||
|
||||
public class Outer {
|
||||
public static class Inner {
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue