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:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2010, 2011, 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 6987555
* @summary JSR 292 unboxing to a boolean value fails on big-endian SPARC
*
* @run main/othervm -Xint -ea -XX:+UnlockDiagnosticVMOptions -XX:+VerifyMethodHandles Test6987555
*/
import java.lang.invoke.*;
public class Test6987555 {
private static final Class CLASS = Test6987555.class;
private static final String NAME = "foo";
private static final boolean DEBUG = false;
public static void main(String[] args) throws Throwable {
testboolean();
testbyte();
testchar();
testshort();
testint();
}
// boolean
static void testboolean() throws Throwable {
doboolean(false);
doboolean(true);
}
static void doboolean(boolean x) throws Throwable {
if (DEBUG) System.out.println("boolean=" + x);
MethodHandle mh1 = MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(boolean.class, boolean.class));
MethodHandle mh2 = mh1.asType(MethodType.methodType(boolean.class, Boolean.class));
boolean a = (boolean) mh1.invokeExact(x);
boolean b = (boolean) mh2.invokeExact(Boolean.valueOf(x));
assert a == b : a + " != " + b;
}
// byte
static void testbyte() throws Throwable {
byte[] a = new byte[] {
Byte.MIN_VALUE,
Byte.MIN_VALUE + 1,
-0x0F,
-1,
0,
1,
0x0F,
Byte.MAX_VALUE - 1,
Byte.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
dobyte(a[i]);
}
}
static void dobyte(byte x) throws Throwable {
if (DEBUG) System.out.println("byte=" + x);
MethodHandle mh1 = MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(byte.class, byte.class));
MethodHandle mh2 = mh1.asType(MethodType.methodType(byte.class, Byte.class));
byte a = (byte) mh1.invokeExact(x);
byte b = (byte) mh2.invokeExact(Byte.valueOf(x));
assert a == b : a + " != " + b;
}
// char
static void testchar() throws Throwable {
char[] a = new char[] {
Character.MIN_VALUE,
Character.MIN_VALUE + 1,
0x000F,
0x00FF,
0x0FFF,
Character.MAX_VALUE - 1,
Character.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
dochar(a[i]);
}
}
static void dochar(char x) throws Throwable {
if (DEBUG) System.out.println("char=" + x);
MethodHandle mh1 = MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(char.class, char.class));
MethodHandle mh2 = mh1.asType(MethodType.methodType(char.class, Character.class));
char a = (char) mh1.invokeExact(x);
char b = (char) mh2.invokeExact(Character.valueOf(x));
assert a == b : a + " != " + b;
}
// short
static void testshort() throws Throwable {
short[] a = new short[] {
Short.MIN_VALUE,
Short.MIN_VALUE + 1,
-0x0FFF,
-0x00FF,
-0x000F,
-1,
0,
1,
0x000F,
0x00FF,
0x0FFF,
Short.MAX_VALUE - 1,
Short.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
doshort(a[i]);
}
}
static void doshort(short x) throws Throwable {
if (DEBUG) System.out.println("short=" + x);
MethodHandle mh1 = MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(short.class, short.class));
MethodHandle mh2 = mh1.asType(MethodType.methodType(short.class, Short.class));
short a = (short) mh1.invokeExact(x);
short b = (short) mh2.invokeExact(Short.valueOf(x));
assert a == b : a + " != " + b;
}
// int
static void testint() throws Throwable {
int[] a = new int[] {
Integer.MIN_VALUE,
Integer.MIN_VALUE + 1,
-0x00000FFF,
-0x000000FF,
-0x0000000F,
-1,
0,
1,
0x0000000F,
0x000000FF,
0x00000FFF,
Integer.MAX_VALUE - 1,
Integer.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
doint(a[i]);
}
}
static void doint(int x) throws Throwable {
if (DEBUG) System.out.println("int=" + x);
MethodHandle mh1 = MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(int.class, int.class));
MethodHandle mh2 = mh1.asType(MethodType.methodType(int.class, Integer.class));
int a = (int) mh1.invokeExact(x);
int b = (int) mh2.invokeExact(Integer.valueOf(x));
assert a == b : a + " != " + b;
}
public static boolean foo(boolean i) { return i; }
public static byte foo(byte i) { return i; }
public static char foo(char i) { return i; }
public static short foo(short i) { return i; }
public static int foo(int i) { return i; }
}

View file

@ -0,0 +1,464 @@
/*
* Copyright (c) 2010, 2011, 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 6991596
* @summary JSR 292 unimplemented adapter_opt_i2i and adapter_opt_l2i on SPARC
*
* @run main/othervm -ea -XX:+UnlockDiagnosticVMOptions -XX:+VerifyMethodHandles Test6991596
*/
import java.lang.invoke.*;
public class Test6991596 {
private static final Class CLASS = Test6991596.class;
private static final String NAME = "foo";
private static final boolean DEBUG = System.getProperty("DEBUG", "false").equals("true");
public static void main(String[] args) throws Throwable {
testboolean();
testbyte();
testchar();
testshort();
testint();
testlong();
}
// Helpers to get various methods.
static MethodHandle getmh1(Class ret, Class arg) throws ReflectiveOperationException {
return MethodHandles.lookup().findStatic(CLASS, NAME, MethodType.methodType(ret, arg));
}
static MethodHandle getmh2(MethodHandle mh1, Class ret, Class arg) {
return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg));
}
static MethodHandle getmh3(MethodHandle mh1, Class ret, Class arg) {
return MethodHandles.explicitCastArguments(mh1, MethodType.methodType(ret, arg));
}
// test adapter_opt_i2i
static void testboolean() throws Throwable {
boolean[] a = new boolean[] {
true,
false
};
for (int i = 0; i < a.length; i++) {
doboolean(a[i]);
}
}
static void doboolean(boolean x) throws Throwable {
if (DEBUG) System.out.println("boolean=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, boolean.class);
// TODO add this for all cases when the bugs are fixed.
//MethodHandle mh3 = getmh3(mh1, boolean.class, boolean.class);
boolean a = (boolean) mh1.invokeExact((boolean) x);
boolean b = (boolean) mh2.invokeExact(x);
//boolean c = mh3.<boolean>invokeExact((boolean) x);
check(x, a, b);
//check(x, c, x);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class );
MethodHandle mh2 = getmh2(mh1, byte.class, boolean.class);
byte a = (byte) mh1.invokeExact((byte) (x ? 1 : 0));
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, boolean.class);
char a = (char) mh1.invokeExact((char) (x ? 1 : 0));
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, boolean.class);
short a = (short) mh1.invokeExact((short) (x ? 1 : 0));
short b = (short) mh2.invokeExact(x);
check(x, a, b);
}
}
static void testbyte() throws Throwable {
byte[] a = new byte[] {
Byte.MIN_VALUE,
Byte.MIN_VALUE + 1,
-0x0F,
-1,
0,
1,
0x0F,
Byte.MAX_VALUE - 1,
Byte.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
dobyte(a[i]);
}
}
static void dobyte(byte x) throws Throwable {
if (DEBUG) System.out.println("byte=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, byte.class);
boolean a = (boolean) mh1.invokeExact((x & 1) == 1);
boolean b = (boolean) mh2.invokeExact(x);
check(x, a, b);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class);
MethodHandle mh2 = getmh2(mh1, byte.class, byte.class);
byte a = (byte) mh1.invokeExact((byte) x);
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, byte.class);
char a = (char) mh1.invokeExact((char) x);
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, byte.class);
short a = (short) mh1.invokeExact((short) x);
short b = (short) mh2.invokeExact(x);
check(x, a, b);
}
}
static void testchar() throws Throwable {
char[] a = new char[] {
Character.MIN_VALUE,
Character.MIN_VALUE + 1,
0x000F,
0x00FF,
0x0FFF,
Character.MAX_VALUE - 1,
Character.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
dochar(a[i]);
}
}
static void dochar(char x) throws Throwable {
if (DEBUG) System.out.println("char=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, char.class);
boolean a = (boolean) mh1.invokeExact((x & 1) == 1);
boolean b = (boolean) mh2.invokeExact(x);
check(x, a, b);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class);
MethodHandle mh2 = getmh2(mh1, byte.class, char.class);
byte a = (byte) mh1.invokeExact((byte) x);
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, char.class);
char a = (char) mh1.invokeExact((char) x);
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, char.class);
short a = (short) mh1.invokeExact((short) x);
short b = (short) mh2.invokeExact(x);
check(x, a, b);
}
}
static void testshort() throws Throwable {
short[] a = new short[] {
Short.MIN_VALUE,
Short.MIN_VALUE + 1,
-0x0FFF,
-0x00FF,
-0x000F,
-1,
0,
1,
0x000F,
0x00FF,
0x0FFF,
Short.MAX_VALUE - 1,
Short.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
doshort(a[i]);
}
}
static void doshort(short x) throws Throwable {
if (DEBUG) System.out.println("short=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, short.class);
boolean a = (boolean) mh1.invokeExact((x & 1) == 1);
boolean b = (boolean) mh2.invokeExact(x);
check(x, a, b);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class);
MethodHandle mh2 = getmh2(mh1, byte.class, short.class);
byte a = (byte) mh1.invokeExact((byte) x);
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, short.class);
char a = (char) mh1.invokeExact((char) x);
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, short.class);
short a = (short) mh1.invokeExact((short) x);
short b = (short) mh2.invokeExact(x);
check(x, a, b);
}
}
static void testint() throws Throwable {
int[] a = new int[] {
Integer.MIN_VALUE,
Integer.MIN_VALUE + 1,
-0x0FFFFFFF,
-0x00FFFFFF,
-0x000FFFFF,
-0x0000FFFF,
-0x00000FFF,
-0x000000FF,
-0x0000000F,
-1,
0,
1,
0x0000000F,
0x000000FF,
0x00000FFF,
0x0000FFFF,
0x000FFFFF,
0x00FFFFFF,
0x0FFFFFFF,
Integer.MAX_VALUE - 1,
Integer.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
doint(a[i]);
}
}
static void doint(int x) throws Throwable {
if (DEBUG) System.out.println("int=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, int.class);
boolean a = (boolean) mh1.invokeExact((x & 1) == 1);
boolean b = (boolean) mh2.invokeExact(x);
check(x, a, b);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class);
MethodHandle mh2 = getmh2(mh1, byte.class, int.class);
byte a = (byte) mh1.invokeExact((byte) x);
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, int.class);
char a = (char) mh1.invokeExact((char) x);
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, int.class);
short a = (short) mh1.invokeExact((short) x);
short b = (short) mh2.invokeExact(x);
assert a == b : a + " != " + b;
check(x, a, b);
}
// int
{
MethodHandle mh1 = getmh1( int.class, int.class);
MethodHandle mh2 = getmh2(mh1, int.class, int.class);
int a = (int) mh1.invokeExact((int) x);
int b = (int) mh2.invokeExact(x);
check(x, a, b);
}
}
// test adapter_opt_l2i
static void testlong() throws Throwable {
long[] a = new long[] {
Long.MIN_VALUE,
Long.MIN_VALUE + 1,
-0x000000000FFFFFFFL,
-0x0000000000FFFFFFL,
-0x00000000000FFFFFL,
-0x000000000000FFFFL,
-0x0000000000000FFFL,
-0x00000000000000FFL,
-0x000000000000000FL,
-1L,
0L,
1L,
0x000000000000000FL,
0x00000000000000FFL,
0x0000000000000FFFL,
0x0000000000000FFFL,
0x000000000000FFFFL,
0x00000000000FFFFFL,
0x0000000000FFFFFFL,
0x000000000FFFFFFFL,
Long.MAX_VALUE - 1,
Long.MAX_VALUE
};
for (int i = 0; i < a.length; i++) {
dolong(a[i]);
}
}
static void dolong(long x) throws Throwable {
if (DEBUG) System.out.println("long=" + x);
// boolean
{
MethodHandle mh1 = getmh1( boolean.class, boolean.class);
MethodHandle mh2 = getmh2(mh1, boolean.class, long.class);
boolean a = (boolean) mh1.invokeExact((x & 1L) == 1L);
boolean b = (boolean) mh2.invokeExact(x);
check(x, a, b);
}
// byte
{
MethodHandle mh1 = getmh1( byte.class, byte.class);
MethodHandle mh2 = getmh2(mh1, byte.class, long.class);
byte a = (byte) mh1.invokeExact((byte) x);
byte b = (byte) mh2.invokeExact(x);
check(x, a, b);
}
// char
{
MethodHandle mh1 = getmh1( char.class, char.class);
MethodHandle mh2 = getmh2(mh1, char.class, long.class);
char a = (char) mh1.invokeExact((char) x);
char b = (char) mh2.invokeExact(x);
check(x, a, b);
}
// short
{
MethodHandle mh1 = getmh1( short.class, short.class);
MethodHandle mh2 = getmh2(mh1, short.class, long.class);
short a = (short) mh1.invokeExact((short) x);
short b = (short) mh2.invokeExact(x);
check(x, a, b);
}
// int
{
MethodHandle mh1 = getmh1( int.class, int.class);
MethodHandle mh2 = getmh2(mh1, int.class, long.class);
int a = (int) mh1.invokeExact((int) x);
int b = (int) mh2.invokeExact(x);
check(x, a, b);
}
}
static void check(boolean x, boolean e, boolean a) { p(z2h(x), z2h(e), z2h(a)); assert e == a : z2h(x) + ": " + z2h(e) + " != " + z2h(a); }
static void check(boolean x, byte e, byte a) { p(z2h(x), i2h(e), i2h(a)); assert e == a : z2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void check(boolean x, int e, int a) { p(z2h(x), i2h(e), i2h(a)); assert e == a : z2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void check(int x, boolean e, boolean a) { p(i2h(x), z2h(e), z2h(a)); assert e == a : i2h(x) + ": " + z2h(e) + " != " + z2h(a); }
static void check(int x, byte e, byte a) { p(i2h(x), i2h(e), i2h(a)); assert e == a : i2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void check(int x, int e, int a) { p(i2h(x), i2h(e), i2h(a)); assert e == a : i2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void check(long x, boolean e, boolean a) { p(l2h(x), z2h(e), z2h(a)); assert e == a : l2h(x) + ": " + z2h(e) + " != " + z2h(a); }
static void check(long x, byte e, byte a) { p(l2h(x), i2h(e), i2h(a)); assert e == a : l2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void check(long x, int e, int a) { p(l2h(x), i2h(e), i2h(a)); assert e == a : l2h(x) + ": " + i2h(e) + " != " + i2h(a); }
static void p(String x, String e, String a) { if (DEBUG) System.out.println(x + ": expected: " + e + ", actual: " + a); }
static String z2h(boolean x) { return x ? "1" : "0"; }
static String i2h(int x) { return Integer.toHexString(x); }
static String l2h(long x) { return Long.toHexString(x); }
// to int
public static boolean foo(boolean i) { return i; }
public static byte foo(byte i) { return i; }
public static char foo(char i) { return i; }
public static short foo(short i) { return i; }
public static int foo(int i) { return i; }
}

View file

@ -0,0 +1,504 @@
/*
* Copyright (c) 2011, 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 6998541
* @summary JSR 292 implement missing return-type conversion for OP_RETYPE_RAW
*
* @run main/othervm -Xbatch
* -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
* -DTest6998541.N=100000 -DTest6998541.KIND=cast Test6998541
* @run main/othervm -Xbatch
* -XX:+UnlockDiagnosticVMOptions -XX:ScavengeRootsInCode=2
* -DTest6998541.N=100000 -DTest6998541.KIND=normal Test6998541
*/
import java.util.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
public class Test6998541 {
private static final Class CLASS = Test6998541.class;
private static final String NAME = "identity";
private static final int N = Math.max(2, Integer.getInteger(CLASS.getSimpleName()+".N", 10000));
private static final String KIND = System.getProperty(CLASS.getSimpleName()+".KIND", "cast");
private static final int BITS = 0x00000201;
private static final boolean DO_CASTS = !KIND.equals("normal");
public static void main(String[] args) throws Throwable {
System.out.println("KIND="+KIND+" DO_CASTS="+DO_CASTS+" N="+N);
doboolean();
dobyte();
dochar();
doshort();
doint();
dolong();
dofloat();
dodouble();
dovoid();
}
private static void doboolean() throws Throwable {
for (int i = 0; i < N; i++) {
boolean2prim(false);
boolean2prim(true);
}
boolean2prim_invalid(true);
}
private static void dobyte() throws Throwable {
byte x = Byte.MIN_VALUE;
for (int i = 0; i < N; i++, x++)
byte2prim(x);
byte2prim_invalid(x);
}
private static void dochar() throws Throwable {
char x = Character.MIN_VALUE;
for (int i = 0; i < N; i++, x++)
char2prim(x);
char2prim_invalid(x);
}
private static void doshort() throws Throwable {
short x = Short.MIN_VALUE;
for (int i = 0; i < N; i++, x++)
short2prim(x);
short2prim_invalid(x);
}
private static void doint() throws Throwable {
int x = Integer.MIN_VALUE;
int D = Integer.MAX_VALUE / (N / 2) | BITS;
for (int i = 0; i < N; i++, x += D) {
int2prim(x);
}
int2prim_invalid(x);
}
private static void dolong() throws Throwable {
long x = Long.MIN_VALUE;
long D = Long.MAX_VALUE / ((long) (N / 2)) | BITS;
for (int i = 0; i < N; i++, x += D)
long2prim(x);
long2prim_invalid(x);
}
private static void dofloat() throws Throwable {
float x = Float.MIN_VALUE;
float D = Float.MAX_VALUE / ((float) (N / 2));
for (int i = 0; i < N; i++, x += D)
float2prim(x);
float2prim_invalid(x);
}
private static void dodouble() throws Throwable {
double x = Double.MIN_VALUE;
double D = Double.MAX_VALUE / ((double) (N / 2));
for (int i = 0; i < N; i++, x += D)
double2prim(x);
double2prim_invalid(x);
}
private static void dovoid() throws Throwable {
for (int i = 0; i < N; i++) {
void2prim(i);
}
void2prim_invalid(0);
// do the other direction here also:
for (int i = 0; i < N; i++) {
prim2void(i);
}
prim2void_invalid(0);
}
private static void assertEquals(Object o, Object o2) {
if (!o.equals(o2))
throw new AssertionError("expected: " + o + ", found: " + o2);
}
private static void fail() {
throw new AssertionError();
}
private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
private static MethodHandle mh(Class ret, Class... args) {
try {
MethodType mt = MethodType.methodType(ret, args);
Class lookupRet = (args.length == 0 ? void.class : args[0]);
MethodHandle mh = lookup.findStatic(CLASS, NAME, mt.changeReturnType(lookupRet));
if (DO_CASTS)
return MethodHandles.explicitCastArguments(mh, mt);
if (canDoAsType(mh.type(), mt))
return mh.asType(mt);
try {
mh.asType(mt);
throw new AssertionError("asType should not succeed: "+mh+" => "+mt);
} catch (WrongMethodTypeException ex) {
// this was a required WMTE
return mh.asType(mt.generic()).asType(mt);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
private static final Class<?>[] NUMERIC_TYPE_WIDENING_ORDER = {
byte.class, short.class, int.class, long.class, float.class, double.class
};
private static boolean canDoAsType(Class<?> src, Class<?> dst) {
if (src == dst) return true;
if (dst == void.class) return true;
if (src == void.class) return true; // allow void->zero
if (!src.isPrimitive() || !dst.isPrimitive()) return true;
// primitive conversion works for asType only when it's widening
if (src == boolean.class || dst == boolean.class) return false;
if (dst == char.class) return false;
if (src == char.class) src = int.class; // can widen char to int
for (Class<?> ntype : NUMERIC_TYPE_WIDENING_ORDER) {
if (src == ntype) return true;
if (dst == ntype) return false;
}
throw new AssertionError("should not reach here: "+src+", "+dst);
}
private static boolean canDoAsType(MethodType mt0, MethodType mt1) {
Class<?> rt0 = mt0.returnType();
Class<?> rt1 = mt1.returnType();
if (!canDoAsType(rt0, rt1)) return false;
int argc = mt0.parameterCount();
if (argc != mt1.parameterCount()) return false;
for (int i = 0; i < argc; i++) {
if (!canDoAsType(mt1.parameterType(i), mt0.parameterType(i)))
return false;
}
return true;
}
private static MethodHandle mh_z(Class ret) { return mh(ret, boolean.class); }
private static final MethodHandle mh_zz = mh_z(boolean.class);
private static final MethodHandle mh_bz = mh_z(byte.class );
private static final MethodHandle mh_cz = mh_z(char.class );
private static final MethodHandle mh_sz = mh_z(short.class );
private static final MethodHandle mh_iz = mh_z(int.class );
private static final MethodHandle mh_jz = mh_z(long.class );
private static final MethodHandle mh_fz = mh_z(float.class );
private static final MethodHandle mh_dz = mh_z(double.class );
private static void boolean2prim(boolean x) throws Throwable {
int i = x ? 1 : 0;
assertEquals( x, (boolean) mh_zz.invokeExact(x)); // boolean -> boolean
if (!DO_CASTS) return;
assertEquals((byte) i, (byte) mh_bz.invokeExact(x)); // boolean -> byte
assertEquals((char) i, (char) mh_cz.invokeExact(x)); // boolean -> char
assertEquals((short) i, (short) mh_sz.invokeExact(x)); // boolean -> short
assertEquals((int) i, (int) mh_iz.invokeExact(x)); // boolean -> int
assertEquals((long) i, (long) mh_jz.invokeExact(x)); // boolean -> long
assertEquals((float) i, (float) mh_fz.invokeExact(x)); // boolean -> float
assertEquals((double) i, (double) mh_dz.invokeExact(x)); // boolean -> double
}
private static void boolean2prim_invalid(boolean x) throws Throwable {
if (DO_CASTS) return;
try { byte y = (byte) mh_bz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> byte
try { char y = (char) mh_cz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> char
try { short y = (short) mh_sz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> short
try { int y = (int) mh_iz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> int
try { long y = (long) mh_jz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> long
try { float y = (float) mh_fz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> float
try { double y = (double) mh_dz.invokeExact(x); fail(); } catch (ClassCastException expected) {} // boolean -> double
}
private static MethodHandle mh_b(Class ret) { return mh(ret, byte.class); }
private static final MethodHandle mh_zb = mh_b(boolean.class);
private static final MethodHandle mh_bb = mh_b(byte.class );
private static final MethodHandle mh_cb = mh_b(char.class );
private static final MethodHandle mh_sb = mh_b(short.class );
private static final MethodHandle mh_ib = mh_b(int.class );
private static final MethodHandle mh_jb = mh_b(long.class );
private static final MethodHandle mh_fb = mh_b(float.class );
private static final MethodHandle mh_db = mh_b(double.class );
private static void byte2prim(byte x) throws Throwable {
assertEquals((byte) x, (byte) mh_bb.invokeExact(x)); // byte -> byte
assertEquals((short) x, (short) mh_sb.invokeExact(x)); // byte -> short
assertEquals((int) x, (int) mh_ib.invokeExact(x)); // byte -> int
assertEquals((long) x, (long) mh_jb.invokeExact(x)); // byte -> long
assertEquals((float) x, (float) mh_fb.invokeExact(x)); // byte -> float
assertEquals((double) x, (double) mh_db.invokeExact(x)); // byte -> double
if (!DO_CASTS) return;
boolean z = ((x & 1) != 0);
assertEquals((char) x, (char) mh_cb.invokeExact(x)); // byte -> char
assertEquals((boolean) z, (boolean) mh_zb.invokeExact(x)); // byte -> boolean
}
private static void byte2prim_invalid(byte x) throws Throwable {
if (DO_CASTS) return;
try { char y = (char) mh_cb.invokeExact(x); fail(); } catch (ClassCastException expected) {} // byte -> char
try { boolean y = (boolean) mh_zb.invokeExact(x); fail(); } catch (ClassCastException expected) {} // byte -> boolean
}
private static MethodHandle mh_c(Class ret) { return mh(ret, char.class); }
private static final MethodHandle mh_zc = mh_c(boolean.class);
private static final MethodHandle mh_bc = mh_c(byte.class );
private static final MethodHandle mh_cc = mh_c(char.class );
private static final MethodHandle mh_sc = mh_c(short.class );
private static final MethodHandle mh_ic = mh_c(int.class );
private static final MethodHandle mh_jc = mh_c(long.class );
private static final MethodHandle mh_fc = mh_c(float.class );
private static final MethodHandle mh_dc = mh_c(double.class );
private static void char2prim(char x) throws Throwable {
assertEquals((char) x, (char) mh_cc.invokeExact(x)); // char -> char
assertEquals((int) x, (int) mh_ic.invokeExact(x)); // char -> int
assertEquals((long) x, (long) mh_jc.invokeExact(x)); // char -> long
assertEquals((float) x, (float) mh_fc.invokeExact(x)); // char -> float
assertEquals((double) x, (double) mh_dc.invokeExact(x)); // char -> double
if (!DO_CASTS) return;
boolean z = ((x & 1) != 0);
assertEquals((boolean) z, (boolean) mh_zc.invokeExact(x)); // char -> boolean
assertEquals((byte) x, (byte) mh_bc.invokeExact(x)); // char -> byte
assertEquals((short) x, (short) mh_sc.invokeExact(x)); // char -> short
}
private static void char2prim_invalid(char x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zc.invokeExact(x); fail(); } catch (ClassCastException expected) {} // char -> boolean
try { byte y = (byte) mh_bc.invokeExact(x); fail(); } catch (ClassCastException expected) {} // char -> byte
try { short y = (short) mh_sc.invokeExact(x); fail(); } catch (ClassCastException expected) {} // char -> short
}
private static MethodHandle mh_s(Class ret) { return mh(ret, short.class); }
private static final MethodHandle mh_zs = mh_s(boolean.class);
private static final MethodHandle mh_bs = mh_s(byte.class );
private static final MethodHandle mh_cs = mh_s(char.class );
private static final MethodHandle mh_ss = mh_s(short.class );
private static final MethodHandle mh_is = mh_s(int.class );
private static final MethodHandle mh_js = mh_s(long.class );
private static final MethodHandle mh_fs = mh_s(float.class );
private static final MethodHandle mh_ds = mh_s(double.class );
private static void short2prim(short x) throws Throwable {
assertEquals((short) x, (short) mh_ss.invokeExact(x)); // short -> short
assertEquals((int) x, (int) mh_is.invokeExact(x)); // short -> int
assertEquals((long) x, (long) mh_js.invokeExact(x)); // short -> long
assertEquals((float) x, (float) mh_fs.invokeExact(x)); // short -> float
assertEquals((double) x, (double) mh_ds.invokeExact(x)); // short -> double
if (!DO_CASTS) return;
boolean z = ((x & 1) != 0);
assertEquals((boolean) z, (boolean) mh_zs.invokeExact(x)); // short -> boolean
assertEquals((byte) x, (byte) mh_bs.invokeExact(x)); // short -> byte
assertEquals((char) x, (char) mh_cs.invokeExact(x)); // short -> char
}
private static void short2prim_invalid(short x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zs.invokeExact(x); fail(); } catch (ClassCastException expected) {} // short -> boolean
try { byte y = (byte) mh_bs.invokeExact(x); fail(); } catch (ClassCastException expected) {} // short -> byte
try { char y = (char) mh_cs.invokeExact(x); fail(); } catch (ClassCastException expected) {} // short -> char
}
private static MethodHandle mh_i(Class ret) { return mh(ret, int.class); }
private static final MethodHandle mh_zi = mh_i(boolean.class);
private static final MethodHandle mh_bi = mh_i(byte.class );
private static final MethodHandle mh_ci = mh_i(char.class );
private static final MethodHandle mh_si = mh_i(short.class );
private static final MethodHandle mh_ii = mh_i(int.class );
private static final MethodHandle mh_ji = mh_i(long.class );
private static final MethodHandle mh_fi = mh_i(float.class );
private static final MethodHandle mh_di = mh_i(double.class );
private static void int2prim(int x) throws Throwable {
assertEquals((int) x, (int) mh_ii.invokeExact(x)); // int -> int
assertEquals((long) x, (long) mh_ji.invokeExact(x)); // int -> long
assertEquals((float) x, (float) mh_fi.invokeExact(x)); // int -> float
assertEquals((double) x, (double) mh_di.invokeExact(x)); // int -> double
if (!DO_CASTS) return;
boolean z = ((x & 1) != 0);
assertEquals((boolean) z, (boolean) mh_zi.invokeExact(x)); // int -> boolean
assertEquals((byte) x, (byte) mh_bi.invokeExact(x)); // int -> byte
assertEquals((char) x, (char) mh_ci.invokeExact(x)); // int -> char
assertEquals((short) x, (short) mh_si.invokeExact(x)); // int -> short
}
private static void int2prim_invalid(int x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zi.invokeExact(x); fail(); } catch (ClassCastException expected) {} // int -> boolean
try { byte y = (byte) mh_bi.invokeExact(x); fail(); } catch (ClassCastException expected) {} // int -> byte
try { char y = (char) mh_ci.invokeExact(x); fail(); } catch (ClassCastException expected) {} // int -> char
try { short y = (short) mh_si.invokeExact(x); fail(); } catch (ClassCastException expected) {} // int -> short
}
private static MethodHandle mh_j(Class ret) { return mh(ret, long.class); }
private static final MethodHandle mh_zj = mh_j(boolean.class);
private static final MethodHandle mh_bj = mh_j(byte.class );
private static final MethodHandle mh_cj = mh_j(char.class );
private static final MethodHandle mh_sj = mh_j(short.class );
private static final MethodHandle mh_ij = mh_j(int.class );
private static final MethodHandle mh_jj = mh_j(long.class );
private static final MethodHandle mh_fj = mh_j(float.class );
private static final MethodHandle mh_dj = mh_j(double.class );
private static void long2prim(long x) throws Throwable {
assertEquals((long) x, (long) mh_jj.invokeExact(x)); // long -> long
assertEquals((float) x, (float) mh_fj.invokeExact(x)); // long -> float
assertEquals((double) x, (double) mh_dj.invokeExact(x)); // long -> double
if (!DO_CASTS) return;
boolean z = ((x & 1) != 0);
assertEquals((boolean)z, (boolean) mh_zj.invokeExact(x)); // long -> boolean
assertEquals((byte) x, (byte) mh_bj.invokeExact(x)); // long -> byte
assertEquals((char) x, (char) mh_cj.invokeExact(x)); // long -> char
assertEquals((short) x, (short) mh_sj.invokeExact(x)); // long -> short
assertEquals((int) x, (int) mh_ij.invokeExact(x)); // long -> int
}
private static void long2prim_invalid(long x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zj.invokeExact(x); fail(); } catch (ClassCastException expected) {} // long -> boolean
try { byte y = (byte) mh_bj.invokeExact(x); fail(); } catch (ClassCastException expected) {} // long -> byte
try { char y = (char) mh_cj.invokeExact(x); fail(); } catch (ClassCastException expected) {} // long -> char
try { short y = (short) mh_sj.invokeExact(x); fail(); } catch (ClassCastException expected) {} // long -> short
try { int y = (int) mh_ij.invokeExact(x); fail(); } catch (ClassCastException expected) {} // long -> int
}
private static MethodHandle mh_f(Class ret) { return mh(ret, float.class); }
private static final MethodHandle mh_zf = mh_f(boolean.class);
private static final MethodHandle mh_bf = mh_f(byte.class );
private static final MethodHandle mh_cf = mh_f(char.class );
private static final MethodHandle mh_sf = mh_f(short.class );
private static final MethodHandle mh_if = mh_f(int.class );
private static final MethodHandle mh_jf = mh_f(long.class );
private static final MethodHandle mh_ff = mh_f(float.class );
private static final MethodHandle mh_df = mh_f(double.class );
private static void float2prim(float x) throws Throwable {
assertEquals((float) x, (float) mh_ff.invokeExact(x)); // float -> float
assertEquals((double) x, (double) mh_df.invokeExact(x)); // float -> double
if (!DO_CASTS) return;
boolean z = (((byte) x & 1) != 0);
assertEquals((boolean) z, (boolean) mh_zf.invokeExact(x)); // float -> boolean
assertEquals((byte) x, (byte) mh_bf.invokeExact(x)); // float -> byte
assertEquals((char) x, (char) mh_cf.invokeExact(x)); // float -> char
assertEquals((short) x, (short) mh_sf.invokeExact(x)); // float -> short
assertEquals((int) x, (int) mh_if.invokeExact(x)); // float -> int
assertEquals((long) x, (long) mh_jf.invokeExact(x)); // float -> long
}
private static void float2prim_invalid(float x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zf.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> boolean
try { byte y = (byte) mh_bf.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> byte
try { char y = (char) mh_cf.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> char
try { short y = (short) mh_sf.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> short
try { int y = (int) mh_if.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> int
try { long y = (long) mh_jf.invokeExact(x); fail(); } catch (ClassCastException expected) {} // float -> long
}
private static MethodHandle mh_d(Class ret) { return mh(ret, double.class); }
private static final MethodHandle mh_zd = mh_d(boolean.class);
private static final MethodHandle mh_bd = mh_d(byte.class );
private static final MethodHandle mh_cd = mh_d(char.class );
private static final MethodHandle mh_sd = mh_d(short.class );
private static final MethodHandle mh_id = mh_d(int.class );
private static final MethodHandle mh_jd = mh_d(long.class );
private static final MethodHandle mh_fd = mh_d(float.class );
private static final MethodHandle mh_dd = mh_d(double.class );
private static void double2prim(double x) throws Throwable {
assertEquals((double) x, (double) mh_dd.invokeExact(x)); // double -> double
if (!DO_CASTS) return;
boolean z = (((byte) x & 1) != 0);
assertEquals((boolean) z, (boolean) mh_zd.invokeExact(x)); // double -> boolean
assertEquals((byte) x, (byte) mh_bd.invokeExact(x)); // double -> byte
assertEquals((char) x, (char) mh_cd.invokeExact(x)); // double -> char
assertEquals((short) x, (short) mh_sd.invokeExact(x)); // double -> short
assertEquals((int) x, (int) mh_id.invokeExact(x)); // double -> int
assertEquals((long) x, (long) mh_jd.invokeExact(x)); // double -> long
assertEquals((float) x, (float) mh_fd.invokeExact(x)); // double -> float
}
private static void double2prim_invalid(double x) throws Throwable {
if (DO_CASTS) return;
try { boolean y = (boolean) mh_zd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> boolean
try { byte y = (byte) mh_bd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> byte
try { char y = (char) mh_cd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> char
try { short y = (short) mh_sd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> short
try { int y = (int) mh_id.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> int
try { long y = (long) mh_jd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> long
try { float y = (float) mh_fd.invokeExact(x); fail(); } catch (ClassCastException expected) {} // double -> float
}
private static final MethodHandle mh_zv = mh(boolean.class);
private static final MethodHandle mh_bv = mh(byte.class );
private static final MethodHandle mh_cv = mh(char.class );
private static final MethodHandle mh_sv = mh(short.class );
private static final MethodHandle mh_iv = mh(int.class );
private static final MethodHandle mh_jv = mh(long.class );
private static final MethodHandle mh_fv = mh(float.class );
private static final MethodHandle mh_dv = mh(double.class );
private static void void2prim(int i) throws Throwable {
assertEquals( false, (boolean) mh_zv.invokeExact()); // void -> boolean
assertEquals((byte) 0, (byte) mh_bv.invokeExact()); // void -> byte
assertEquals((char) 0, (char) mh_cv.invokeExact()); // void -> char
assertEquals((short) 0, (short) mh_sv.invokeExact()); // void -> short
assertEquals( 0, (int) mh_iv.invokeExact()); // void -> int
assertEquals( 0L, (long) mh_jv.invokeExact()); // void -> long
assertEquals( 0.0f, (float) mh_fv.invokeExact()); // void -> float
assertEquals( 0.0d, (double) mh_dv.invokeExact()); // void -> double
}
private static void void2prim_invalid(double x) throws Throwable {
// no cases
}
private static MethodHandle mh_v(Class arg) { return mh(void.class, arg); }
private static final MethodHandle mh_vz = mh_v(boolean.class);
private static final MethodHandle mh_vb = mh_v(byte.class );
private static final MethodHandle mh_vc = mh_v(char.class );
private static final MethodHandle mh_vs = mh_v(short.class );
private static final MethodHandle mh_vi = mh_v(int.class );
private static final MethodHandle mh_vj = mh_v(long.class );
private static final MethodHandle mh_vf = mh_v(float.class );
private static final MethodHandle mh_vd = mh_v(double.class );
private static void prim2void(int x) throws Throwable {
boolean z = ((x & 1) != 0);
mh_vz.invokeExact( z); // boolean -> void
mh_vb.invokeExact((byte) x); // byte -> void
mh_vc.invokeExact((char) x); // char -> void
mh_vs.invokeExact((short) x); // short -> void
mh_vi.invokeExact((int) x); // int -> void
mh_vj.invokeExact((long) x); // long -> void
mh_vf.invokeExact((float) x); // float -> void
mh_vd.invokeExact((double) x); // double -> void
}
private static void prim2void_invalid(int x) throws Throwable {
// no cases
}
private static boolean identity(boolean v) { return v; }
private static byte identity(byte v) { return v; }
private static char identity(char v) { return v; }
private static short identity(short v) { return v; }
private static int identity(int v) { return v; }
private static long identity(long v) { return v; }
private static float identity(float v) { return v; }
private static double identity(double v) { return v; }
private static void identity() {}
}

View file

@ -0,0 +1,237 @@
/*
* Copyright (c) 2013, 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 7087570
* @summary REF_invokeSpecial DMHs (which are unusual) get marked explicitly; tweak the MHI to use this bit
*
* @run main Test7087570
*/
import java.lang.invoke.*;
import java.lang.reflect.*;
import java.util.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import static java.lang.invoke.MethodHandleInfo.*;
public class Test7087570 {
private static final TestMethodData[] TESTS = new TestMethodData[] {
// field accessors
data(DummyFieldHolder.class, "instanceField", getterMethodType(String.class), DummyFieldHolder.class, REF_getField),
data(DummyFieldHolder.class, "instanceField", setterMethodType(String.class), DummyFieldHolder.class, REF_putField),
data(DummyFieldHolder.class, "staticField", getterMethodType(Integer.class), DummyFieldHolder.class, REF_getStatic),
data(DummyFieldHolder.class, "staticField", setterMethodType(Integer.class), DummyFieldHolder.class, REF_putStatic),
data(DummyFieldHolder.class, "instanceByteField", getterMethodType(byte.class), DummyFieldHolder.class, REF_getField),
data(DummyFieldHolder.class, "instanceByteField", setterMethodType(byte.class), DummyFieldHolder.class, REF_putField),
// REF_invokeVirtual
data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeVirtual),
// REF_invokeVirtual strength-reduced to REF_invokeSpecial,
// test if it normalizes back to REF_invokeVirtual in MethodHandleInfo as expected
data(String.class, "hashCode", methodType(int.class), String.class, REF_invokeVirtual),
// REF_invokeStatic
data(Collections.class, "sort", methodType(void.class, List.class), Collections.class, REF_invokeStatic),
data(Arrays.class, "asList", methodType(List.class, Object[].class), Arrays.class, REF_invokeStatic), // varargs case
// REF_invokeSpecial
data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeSpecial),
// REF_newInvokeSpecial
data(String.class, "<init>", methodType(void.class, char[].class), String.class, REF_newInvokeSpecial),
data(DummyFieldHolder.class, "<init>", methodType(void.class, byte.class, Long[].class), DummyFieldHolder.class, REF_newInvokeSpecial), // varargs case
// REF_invokeInterface
data(List.class, "size", methodType(int.class), List.class, REF_invokeInterface)
};
public static void main(String... args) throws Throwable {
testWithLookup();
testWithUnreflect();
}
private static void doTest(MethodHandle mh, TestMethodData testMethod) {
MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
referenceKindToString(testMethod.referenceKind),
referenceKindToString(mhi.getReferenceKind()));
assertEquals(testMethod.name, mhi.getName());
assertEquals(testMethod.methodType, mhi.getMethodType());
assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
assertRefKindEquals(testMethod.referenceKind, mhi.getReferenceKind());
}
private static void testWithLookup() throws Throwable {
for (TestMethodData testMethod : TESTS) {
MethodHandle mh = lookupFrom(testMethod);
doTest(mh, testMethod);
}
}
private static void testWithUnreflect() throws Throwable {
for (TestMethodData testMethod : TESTS) {
MethodHandle mh = unreflectFrom(testMethod);
doTest(mh, testMethod);
}
}
private static MethodType getterMethodType(Class<?> clazz) {
return methodType(clazz);
}
private static MethodType setterMethodType(Class<?> clazz) {
return methodType(void.class, clazz);
}
private static final Lookup LOOKUP = lookup();
private static class TestMethodData {
final Class<?> clazz;
final String name;
final MethodType methodType;
final Class<?> declaringClass;
final int referenceKind; // the nominal refKind
public TestMethodData(Class<?> clazz, String name,
MethodType methodType, Class<?> declaringClass,
int referenceKind) {
this.clazz = clazz;
this.name = name;
this.methodType = methodType;
this.declaringClass = declaringClass;
this.referenceKind = referenceKind;
}
}
private static TestMethodData data(Class<?> clazz, String name,
MethodType methodType, Class<?> declaringClass,
int referenceKind) {
return new TestMethodData(clazz, name, methodType, declaringClass, referenceKind);
}
private static MethodHandle lookupFrom(TestMethodData testMethod)
throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
switch (testMethod.referenceKind) {
case REF_getField:
return LOOKUP.findGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
case REF_putField:
return LOOKUP.findSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
case REF_getStatic:
return LOOKUP.findStaticGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
case REF_putStatic:
return LOOKUP.findStaticSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
case REF_invokeVirtual:
case REF_invokeInterface:
return LOOKUP.findVirtual(testMethod.clazz, testMethod.name, testMethod.methodType);
case REF_invokeStatic:
return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
case REF_invokeSpecial:
Class<?> thisClass = LOOKUP.lookupClass();
MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
noteInvokeSpecial(smh);
return smh;
case REF_newInvokeSpecial:
return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
default:
throw new Error("ERROR: unexpected referenceKind in test data");
}
}
private static MethodHandle unreflectFrom(TestMethodData testMethod)
throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
switch (testMethod.referenceKind) {
case REF_getField:
case REF_getStatic: {
Field f = testMethod.clazz.getDeclaredField(testMethod.name);
return LOOKUP.unreflectGetter(f);
}
case REF_putField:
case REF_putStatic: {
Field f = testMethod.clazz.getDeclaredField(testMethod.name);
return LOOKUP.unreflectSetter(f);
}
case REF_invokeVirtual:
case REF_invokeStatic:
case REF_invokeInterface: {
Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
return LOOKUP.unreflect(m);
}
case REF_invokeSpecial: {
Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
Class<?> thisClass = LOOKUP.lookupClass();
MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
noteInvokeSpecial(smh);
return smh;
}
case REF_newInvokeSpecial: {
Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
return LOOKUP.unreflectConstructor(c);
}
default:
throw new Error("ERROR: unexpected referenceKind in test data");
}
}
private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
private static void noteInvokeSpecial(MethodHandle mh) {
specialMethodHandles.add(mh);
assert(isInvokeSpecial(mh));
}
private static boolean isInvokeSpecial(MethodHandle mh) {
return specialMethodHandles.contains(mh);
}
private static void assertRefKindEquals(int expect, int observed) {
if (expect == observed) return;
String msg = "expected " + referenceKindToString(expect) +
" but observed " + referenceKindToString(observed);
System.out.println("FAILED: " + msg);
throw new AssertionError(msg);
}
private static void assertEquals(Object expect, Object observed) {
if (java.util.Objects.equals(expect, observed)) return;
String msg = "expected " + expect + " but observed " + observed;
System.out.println("FAILED: " + msg);
throw new AssertionError(msg);
}
}
class DummyFieldHolder {
public static Integer staticField;
public String instanceField;
public byte instanceByteField;
public DummyFieldHolder(byte unused1, Long... unused2) {
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2012, 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.
*/
/*
7157574 method handles returned by reflective lookup API sometimes have wrong receiver type
When an inherited non-static field or method is looked up in a class C using Lookup.findVirtual(C...), etc., the JSR 292 API, the first argument of the resulting method handle must be the receiver ('this'), and must be the requested class (or more specific, in the case of findSpecial or a lookup of a protected method).
But currently, if a supertype T defines the looked-up method or field and C inherits it, the returned method handle might have the more specific initial type T.
The relevant javadoc (and 292 spec.) is as follows:
* The formal parameter {@code this} stands for the self-reference of type {@code C};
* if it is present, it is always the leading argument to the method handle invocation.
* (In the case of some {@code protected} members, {@code this} may be
* restricted in type to the lookup class; see below.)
Because of this bug, all of the assertions fail in the following example:
*/
/* @test
* @bug 7157574
* @summary method handles returned by reflective lookup API sometimes have wrong receiver type
*
* @run main Test7157574
*/
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
public class Test7157574 {
interface Intf { void ig1(); void ig2(); void ig3(); void ig4(); void m1(); }
abstract static class Super implements Intf { public abstract void m2(); public int f2; }
abstract static class Sub extends Super { }
public static void main(String... av) throws Throwable {
MethodHandle m1 = lookup().findVirtual(Sub.class, "m1", methodType(void.class));
System.out.println(m1);
MethodHandle m2 = lookup().findVirtual(Sub.class, "m2", methodType(void.class));
System.out.println(m2);
MethodHandle f2 = lookup().findGetter(Sub.class, "f2", int.class);
System.out.println(f2);
MethodHandle f2s = lookup().findSetter(Sub.class, "f2", int.class);
System.out.println(f2s);
MethodHandle chc = lookup().findVirtual(Sub.class, "hashCode", methodType(int.class));
System.out.println(chc);
MethodHandle ihc = lookup().findVirtual(Intf.class, "hashCode", methodType(int.class));
System.out.println(ihc);
assertEquals(Sub.class, m1.type().parameterType(0));
assertEquals(Sub.class, m2.type().parameterType(0));
assertEquals(Sub.class, f2.type().parameterType(0));
assertEquals(Sub.class, f2s.type().parameterType(0));
assertEquals(Sub.class, chc.type().parameterType(0));
assertEquals(Intf.class, ihc.type().parameterType(0));
// test the MHs on a concrete version of Sub
class C extends Sub {
public void m1() { this.f2 = -1; }
public void m2() { this.f2 = -2; }
// Pack the vtable of Intf with leading junk:
private void ig() { throw new RuntimeException(); }
public void ig1() { ig(); }
public void ig2() { ig(); }
public void ig3() { ig(); }
public void ig4() { ig(); }
}
testConcrete(new C(), m1, m2, f2, f2s, chc, ihc);
}
private static void testConcrete(Sub s,
MethodHandle m1, MethodHandle m2,
MethodHandle f2, MethodHandle f2s,
MethodHandle chc, MethodHandle ihc
) throws Throwable {
s.f2 = 0;
m1.invokeExact(s);
assertEquals(-1, s.f2);
m2.invokeExact(s);
assertEquals(-2, s.f2);
s.f2 = 2;
assertEquals(2, (int) f2.invokeExact(s));
f2s.invokeExact(s, 0);
assertEquals(0, s.f2);
assertEquals(s.hashCode(), (int) chc.invokeExact(s));
assertEquals(s.hashCode(), (int) ihc.invokeExact((Intf)s));
}
private static void assertEquals(Object expect, Object observe) {
if (java.util.Objects.equals(expect, observe)) return;
String msg = ("expected "+expect+" but observed "+observe);
System.out.println("FAILED: "+msg);
throw new AssertionError(msg);
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2012, 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 7196190
* @summary Improve method of handling MethodHandles
*
* @run main/othervm ClassForNameTest
*/
import java.lang.invoke.*;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ClassForNameTest {
static final String NAME = ClassForNameTest.class.getName();
public static void main(String[] args) throws Throwable {
{
final MethodType mt = MethodType.methodType(Class.class, String.class);
final MethodHandle mh = MethodHandles.lookup()
.findStatic(Class.class, "forName", mt);
Class.forName(NAME);
mh.invoke(NAME);
mh.bindTo(NAME).invoke();
mh.invokeWithArguments(Arrays.asList(NAME));
mh.invokeWithArguments(NAME);
Class cls = (Class) mh.invokeExact(NAME);
}
{
final Method fnMethod = Class.class.getMethod("forName", String.class);
final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
final MethodHandle mh = MethodHandles.lookup()
.findVirtual(Method.class, "invoke", mt)
.bindTo(fnMethod);
fnMethod.invoke(null, NAME);
mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
mh.invoke(null, new Object[]{NAME});
mh.invokeWithArguments(null, new Object[]{NAME});
mh.invokeWithArguments(Arrays.asList(null, new Object[]{NAME}));
Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
}
{
final Method fnMethod = Class.class.getMethod("forName", String.class);
final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
final MethodHandle mh = MethodHandles.lookup()
.bind(fnMethod, "invoke", mt);
mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
mh.invoke(null, new Object[]{NAME});
mh.invokeWithArguments(null, NAME);
mh.invokeWithArguments(Arrays.asList(null, NAME));
Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
}
{
final Method fnMethod = Class.class.getMethod("forName", String.class);
final MethodHandle mh = MethodHandles.lookup().unreflect(fnMethod);
mh.bindTo(NAME).invoke();
mh.invoke(NAME);
mh.invokeWithArguments(NAME);
mh.invokeWithArguments(Arrays.asList(NAME));
Class cls = (Class) mh.invokeExact(NAME);
}
System.out.println("TEST PASSED");
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2013, 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 8009222
* @summary java.lang.IllegalArgumentException: not invocable, no method type
* when attempting to get getter method handle for a static field
*
* @run main/othervm Test8009222
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Objects;
interface Intf {
static int i = 0;
}
public class Test8009222 {
public static void main(String[] args) throws Exception {
Objects.requireNonNull(MethodHandles.lookup()
.findStaticGetter(Intf.class, "i", int.class));
System.out.println("TEST PASSED");
}
}

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2013, 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.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.ClassFile;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
public class BogoLoader extends ClassLoader {
/**
* Use this property to verify that the desired classloading is happening.
*/
private final boolean verbose = Boolean.getBoolean("bogoloader.verbose");
/**
* Use this property to disable replacement for testing purposes.
*/
private final boolean noReplace = Boolean.getBoolean("bogoloader.noreplace");
/**
* Set of class names that should be loaded with this loader.
* Others are loaded with the system class loader, except for those
* that are transformed.
*/
private Set<String> nonSystem;
/**
* Map from class names to a bytecode transformer factory.
*/
private Map<String, ClassTransform> replaced;
/**
* Keep track (not terribly efficiently) of which classes have already
* been loaded by this class loader.
*/
private final Vector<String> history = new Vector<String>();
private boolean useSystemLoader(String name) {
return ! nonSystem.contains(name) && ! replaced.containsKey(name);
}
public BogoLoader(Set<String> non_system, Map<String, ClassTransform> replaced) {
super(Thread.currentThread().getContextClassLoader());
this.nonSystem = non_system;
this.replaced = replaced;
}
private byte[] readResource(String className) throws IOException {
return readResource(className, "class");
}
private byte[] readResource(String className, String suffix) throws IOException {
// Note to the unwary -- "/" works on Windows, leave it alone.
String fileName = className.replace('.', '/') + "." + suffix;
InputStream origStream = getResourceAsStream(fileName);
if (origStream == null) {
throw new IOException("Resource not found : " + fileName);
}
BufferedInputStream stream = new java.io.BufferedInputStream(origStream);
byte[] data = new byte[stream.available()];
int how_many = stream.read(data);
// Really ought to deal with the corner cases of stream.available()
return data;
}
protected byte[] getClass(String name) throws ClassNotFoundException,
IOException {
return readResource(name, "class");
}
/**
* Loads the named class from the system class loader unless
* the name appears in either replaced or nonSystem.
* nonSystem classes are loaded into this classloader,
* and replaced classes get their content from the specified array
* of bytes (and are also loaded into this classloader).
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz;
if (history.contains(name)) {
Class<?> c = this.findLoadedClass(name);
return c;
}
if (useSystemLoader(name)) {
clazz = findSystemClass(name);
if (verbose) System.err.println("Loading system class " + name);
} else {
history.add(name);
try {
if (verbose) {
System.err.println("Loading classloader class " + name);
}
byte[] classData = getClass(name);
boolean expanded = false;
if (!noReplace && replaced.containsKey(name)) {
if (verbose) {
System.err.println("Replacing class " + name);
}
var cf = ClassFile.of();
classData = cf.transformClass(cf.parse(classData), replaced.get(name));
}
clazz = defineClass(name, classData, 0, classData.length);
} catch (java.io.EOFException ioe) {
throw new ClassNotFoundException(
"IO Exception in reading class : " + name + " ", ioe);
} catch (ClassFormatError ioe) {
throw new ClassNotFoundException(
"ClassFormatError in reading class file: ", ioe);
} catch (IOException ioe) {
throw new ClassNotFoundException(
"IO Exception in reading class file: ", ioe);
}
}
if (clazz == null) {
throw new ClassNotFoundException(name);
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.InvocationTargetException;
/**
* Tries various ways of ultimately invoking MethodSupplier.m(),
* except that m has been made inaccessible and some exception should be
* thrown instead.
*/
public class InvokeSeveralWays {
public static int test(String args[], Class expected) throws Exception {
int failures = 0;
try {
Class.forName("Invoker").getMethod("invoke").invoke(null);
System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier");
failures++;
} catch (InvocationTargetException e) {
Throwable c = e.getCause();
if (expected.isInstance(c))
System.out.println("EXPECTED: " + expected.getName() + ", "+ c);
else {
failures++;
System.out.println("FAIL: Unexpected wrapped exception " + c);
e.printStackTrace(System.out);
}
} catch (Throwable e) {
failures++;
System.out.println("FAIL: Unexpected exception has been caught " + e);
e.printStackTrace(System.out);
}
System.out.println();
try {
Class.forName("Invoker").getMethod("invoke2").invoke(null);
System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier");
failures++;
} catch (InvocationTargetException e) {
Throwable c = e.getCause();
if (expected.isInstance(c))
System.out.println("EXPECTED: " + expected.getName() + ", "+ c);
else {
failures++;
System.out.println("FAIL: Unexpected wrapped exception " + c);
e.printStackTrace(System.out);
}
} catch (Throwable e) {
failures++;
System.out.println("FAIL: Unexpected exception has been caught " + e);
e.printStackTrace(System.out);
}
System.out.println();
try {
Invoker.invoke();
System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier");
failures++;
} catch (Throwable e) {
if (expected.isInstance(e))
System.out.println("EXPECTED: " + expected.getName() + ", "+ e);
else {
failures++;
System.out.println("FAIL: Unexpected exception has been caught " + e);
e.printStackTrace(System.out);
}
}
System.out.println();
try {
Invoker.invoke2();
System.out.println("FAIL: No exception throw, probably failed to load modified bytecodes for MethodSupplier");
failures++;
} catch (Throwable e) {
if (expected.isInstance(e))
System.out.println("EXPECTED: " + expected.getName() + ", "+ e);
else {
failures++;
System.out.println("FAIL: Unexpected exception has been caught " + e);
e.printStackTrace(System.out);
}
}
System.out.println();
if (failures > 0) {
System.out.println("Saw " + failures + " failures");
}
return failures;
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2013, 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 Invoker {
/**
* Use a method handle to invoke m.
*/
public static void invoke() {
MyFunctionalInterface fi = null;
fi = new MethodSupplier()::<Integer, String, Long>m;
fi.invokeMethodReference();
}
/**
* Invoke m directly.
*/
public static void invoke2() {
MethodSupplier ms = new MethodSupplier();
ms.m();
}
}
interface MyFunctionalInterface {
void invokeMethodReference();
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2013, 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.
*/
/*
* @test
* @bug 8022701
* @summary Illegal access exceptions via methodhandle invocations threw wrong error.
* @compile -XDignore.symbol.file BogoLoader.java InvokeSeveralWays.java MHIllegalAccess.java MethodSupplier.java
* @run main/othervm MHIllegalAccess
*/
import java.lang.classfile.AccessFlags;
import java.lang.classfile.ClassTransform;
import java.lang.classfile.MethodModel;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import static java.lang.classfile.ClassFile.ACC_PRIVATE;
import static java.lang.classfile.ClassFile.ACC_PROTECTED;
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
public class MHIllegalAccess {
public static void main(String[] args) throws Throwable {
System.out.println("Classpath is " + System.getProperty("java.class.path"));
System.out.println();
/**
* Make method m be private to provoke an IllegalAccessError.
*/
var privatize = ClassTransform.transformingMethods(m -> m.methodName().equalsString("m"), (mb, me) -> {
if (me instanceof AccessFlags af) {
mb.withFlags((af.flagsMask() | ACC_PRIVATE) & ~ (ACC_PUBLIC | ACC_PROTECTED));
} else {
mb.accept(me);
}
});
/**
* Rename method m as nemo to provoke a NoSuchMethodError.
*/
ClassTransform changeName = (cb, ce) -> {
if (ce instanceof MethodModel mm && mm.methodName().equalsString("m")) {
cb.withMethod("nemo", mm.methodTypeSymbol(), mm.flags().flagsMask(), mm::forEach);
} else {
cb.accept(ce);
}
};
int failures = 0;
failures += testOneError(privatize, args, IllegalAccessError.class);
failures += testOneError(changeName, args, NoSuchMethodError.class);
if (failures > 0) {
System.out.println("Saw " + failures + " failures, see standard out for details");
throw new Error("FAIL test");
}
}
/**
*
* @param vm VisitorMaker, to be stored in a table and passed to a BogoLoader
* @param args A copy of the main args, to be passed on to InvokeSeveralWays.test
* @param expected The class of the exception that should be thrown after
* attempted invocation of MethodSupplier.m.
* @throws ClassNotFoundException
* @throws Throwable
*/
private static int testOneError(ClassTransform vm, String[] args, Class<?> expected) throws ClassNotFoundException, Throwable {
var replace = new HashMap<String, ClassTransform>();
replace.put("MethodSupplier", vm);
HashSet<String> in_bogus = new HashSet<String>();
in_bogus.add("InvokeSeveralWays");
in_bogus.add("MyFunctionalInterface");
in_bogus.add("Invoker");
BogoLoader bl = new BogoLoader(in_bogus, replace);
Class<?> isw = bl.loadClass("InvokeSeveralWays");
Object[] arg_for_args = new Object[2];
arg_for_args[0] = args;
arg_for_args[1] = expected;
try {
Object result = isw.getMethod("test", String[].class, Class.class).invoke(null, arg_for_args);
return (Integer)result;
} catch (InvocationTargetException e) {
Throwable th = e.getCause();
throw th == null ? e : th;
}
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2013, 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.
*/
/*
* Note: this class is only used in this form to facilitate compilation of the
* rest of the code. Before execution, the bytecodes are mutilated in a
* BogoLoader to either make m be private or have a different name.
*/
public class MethodSupplier {
public void m() {
System.out.println("good");
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2016, 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 8147078
* @run junit/othervm -ea -esa Test8147078
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import static java.lang.invoke.MethodType.methodType;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class Test8147078 {
static int target(int x) {
throw new RuntimeException("ieps");
}
static int handler(String s, int x) {
return 4*x;
}
static final MethodHandle MH_target;
static final MethodHandle MH_handler;
static final MethodHandle MH_catchException;
static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
static {
try {
Class<Test8147078> C = Test8147078.class;
MH_target = LOOKUP.findStatic(C, "target", methodType(int.class, int.class));
MH_handler = LOOKUP.findStatic(C, "handler", methodType(int.class, String.class, int.class));
MH_catchException = LOOKUP.findStatic(MethodHandles.class, "catchException",
methodType(MethodHandle.class, MethodHandle.class, Class.class, MethodHandle.class));
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
@Test
public void testNoExceptionType() {
var cce = assertThrows(ClassCastException.class, () -> {
MethodHandle eek = (MethodHandle) MH_catchException.invoke(MH_target, String.class, MH_handler);
});
assertEquals("java.lang.String", cce.getMessage());
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2017, 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 8177146
* @run junit/othervm TestMethodHandleBind
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import static java.lang.invoke.MethodHandles.lookup;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class TestMethodHandleBind extends pkg.A {
static class B extends TestMethodHandleBind {}
@Test
public void testInstanceOfCallerClass() throws Throwable {
MethodHandle bound = lookup().bind(new TestMethodHandleBind() , "m1", MethodType.methodType(String.class));
String x = (String)bound.invoke();
assertEquals(this.getClass().getSimpleName(), x);
}
@Test
public void testInstanceOfCallerSubclass() throws Throwable {
MethodHandle bound = lookup().bind(new B() , "m1", MethodType.methodType(String.class));
// MethodHandle bound = lookup().findVirtual(B.class, "m1", MethodType.methodType(String.class)).bindTo(new B());
String x = (String)bound.invoke();
assertEquals("B", x);
}
@Test
public void testInstanceOfReceiverClass() throws Throwable {
assertThrows(IllegalAccessException.class, () -> lookup().bind(new pkg.A() , "m1", MethodType.methodType(String.class)));
}
@Test
public void testPublicMethod() throws Throwable {
MethodHandle bound = lookup().bind(new pkg.A() , "m2", MethodType.methodType(String.class));
String x = (String)bound.invoke();
assertEquals("A", x);
}
@Test
public void testPublicMethod2() throws Throwable {
MethodHandle bound = lookup().bind(new TestMethodHandleBind(), "m2", MethodType.methodType(String.class));
String x = (String)bound.invoke();
assertEquals(this.getClass().getSimpleName(), x);
}
@Test
public void testInstanceOfCallerClassVarargs() throws Throwable {
MethodHandle bound = lookup().bind(new TestMethodHandleBind() , "m3", MethodType.methodType(String.class, String[].class));
String x = (String)bound.invoke("a", "b", "c");
assertEquals(this.getClass().getSimpleName() + "abc", x);
}
@Test
public void testInstanceOfReceiverClassVarargs() throws Throwable {
assertThrows(IllegalAccessException.class, () -> lookup().bind(new pkg.A(), "m3", MethodType.methodType(String.class, String[].class)));
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017, 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 pkg;
public class A {
protected String m1() {
return this.getClass().getSimpleName();
}
public String m2() {
return this.getClass().getSimpleName();
}
protected String m3(String... args) {
StringBuilder sb = new StringBuilder();
for (String s : args)
sb.append(s);
return this.getClass().getSimpleName() + sb.toString();
}
}

View file

@ -0,0 +1,687 @@
/*
* Copyright (c) 2012, 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
* @summary test access checking by java.lang.invoke.MethodHandles.Lookup
* @compile AccessControlTest.java AccessControlTest_subpkg/Acquaintance_remote.java
* @run junit/othervm test.java.lang.invoke.AccessControlTest
*/
package test.java.lang.invoke;
import java.lang.invoke.*;
import java.lang.reflect.*;
import java.lang.reflect.Modifier;
import java.util.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodHandles.Lookup.*;
import static java.lang.invoke.MethodType.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote;
/**
* Test many combinations of Lookup access and cross-class lookupStatic.
* @author jrose
*/
public class AccessControlTest {
static final Class<?> THIS_CLASS = AccessControlTest.class;
// How much output?
static int verbosity = 0;
static {
String vstr = System.getProperty(THIS_CLASS.getSimpleName()+".verbosity");
if (vstr == null)
vstr = System.getProperty(THIS_CLASS.getName()+".verbosity");
if (vstr != null) verbosity = Integer.parseInt(vstr);
}
private class LookupCase implements Comparable<LookupCase> {
final Lookup lookup;
final Class<?> lookupClass;
final Class<?> prevLookupClass;
final int lookupModes;
public LookupCase(Lookup lookup) {
this.lookup = lookup;
this.lookupClass = lookup.lookupClass();
this.prevLookupClass = lookup.previousLookupClass();
this.lookupModes = lookup.lookupModes();
assertEquals(lookupString(), lookup.toString());
numberOf(lookupClass().getClassLoader()); // assign CL#
}
public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {
this.lookup = null;
this.lookupClass = lookupClass;
this.prevLookupClass = prevLookupClass;
this.lookupModes = lookupModes;
numberOf(lookupClass().getClassLoader()); // assign CL#
}
public final Class<?> lookupClass() { return lookupClass; }
public final Class<?> prevLookupClass() { return prevLookupClass; }
public final int lookupModes() { return lookupModes; }
public Lookup lookup() { lookup.getClass(); return lookup; }
@Override
public int compareTo(LookupCase that) {
Class<?> c1 = this.lookupClass();
Class<?> c2 = that.lookupClass();
Class<?> p1 = this.prevLookupClass();
Class<?> p2 = that.prevLookupClass();
if (c1 != c2) {
int cmp = c1.getName().compareTo(c2.getName());
if (cmp != 0) return cmp;
cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
assertNotEquals(0, cmp);
return cmp;
} else if (p1 != p2){
if (p1 == null)
return 1;
else if (p2 == null)
return -1;
int cmp = p1.getName().compareTo(p2.getName());
if (cmp != 0) return cmp;
cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());
assertNotEquals(0, cmp);
return cmp;
}
return -(this.lookupModes() - that.lookupModes());
}
@Override
public boolean equals(Object that) {
return (that instanceof LookupCase && equals((LookupCase)that));
}
public boolean equals(LookupCase that) {
return (this.lookupClass() == that.lookupClass() &&
this.prevLookupClass() == that.prevLookupClass() &&
this.lookupModes() == that.lookupModes());
}
@Override
public int hashCode() {
return lookupClass().hashCode() + (lookupModes() * 31);
}
/** Simulate all assertions in the spec. for Lookup.toString. */
private String lookupString() {
String name = lookupClass.getName();
if (prevLookupClass != null)
name += "/" + prevLookupClass.getName();
String suffix = "";
if (lookupModes == 0)
suffix = "/noaccess";
else if (lookupModes == PUBLIC)
suffix = "/public";
else if (lookupModes == UNCONDITIONAL)
suffix = "/publicLookup";
else if (lookupModes == (PUBLIC|MODULE))
suffix = "/module";
else if (lookupModes == (PUBLIC|PACKAGE)
|| lookupModes == (PUBLIC|MODULE|PACKAGE))
suffix = "/package";
else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
suffix = "/private";
else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED)
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|ORIGINAL))
suffix = "";
else
suffix = "/#"+Integer.toHexString(lookupModes);
return name+suffix;
}
/** Simulate all assertions from the spec. for Lookup.in:
* <hr>
* Creates a lookup on the specified new lookup class.
* [A1] The resulting object will report the specified
* class as its own {@link #lookupClass lookupClass}.
* [A1-a] However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
* [A2] If the new lookup class is not the same as the old lookup class,
* then {@link #ORIGINAL ORIGINAL} access is lost.
* [A3] If the new lookup class is in a different module from the old one,
* i.e. {@link #MODULE MODULE} access is lost.
* [A4] If the new lookup class is in a different package
* than the old one, protected and default (package) members will not be accessible,
* i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
* [A5] If the new lookup class is not within the same package member
* as the old one, private members will not be accessible, and protected members
* will not be accessible by virtue of inheritance,
* i.e. {@link #PRIVATE PRIVATE} access is lost.
* (Protected members may continue to be accessible because of package sharing.)
* [A6] If the new lookup class is not
* {@linkplain #accessClass(Class) accessible} to this lookup,
* then no members, not even public members, will be accessible
* i.e. all access modes are lost.
* [A7] If the new lookup class, the old lookup class and the previous lookup class
* are all in different modules i.e. teleporting to a third module,
* all access modes are lost.
* <p>
* The new previous lookup class is chosen as follows:
* [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
* the new previous lookup class is {@code null}.
* [A9] If the new lookup class is in the same module as the old lookup class,
* the new previous lookup class is the old previous lookup class.
* [A10] If the new lookup class is in a different module from the old lookup class,
* the new previous lookup class is the old lookup class.
*
* Other than the above cases, the new lookup will have the same
* access capabilities as the original. [A11]
* <hr>
*/
public LookupCase in(Class<?> c2) {
Class<?> c1 = lookupClass();
Module m1 = c1.getModule();
Module m2 = c2.getModule();
Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();
int modes1 = lookupModes();
int changed = 0;
// for the purposes of access control then treat classes in different unnamed
// modules as being in the same module.
boolean sameModule = (m1 == m2) ||
(!m1.isNamed() && !m2.isNamed());
boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
c1.getPackageName().equals(c2.getPackageName()));
boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
boolean sameClass = (c1 == c2);
assertTrue(samePackage || !sameTopLevel);
assertTrue(sameTopLevel || !sameClass);
boolean accessible = sameClass;
if ((modes1 & PACKAGE) != 0) accessible |= samePackage;
if ((modes1 & PUBLIC ) != 0) {
if (isModuleAccessible(c2))
accessible |= (c2.getModifiers() & PUBLIC) != 0;
else
accessible = false;
}
if ((modes1 & UNCONDITIONAL) != 0) {
if (m2.isExported(c2.getPackageName()))
accessible |= (c2.getModifiers() & PUBLIC) != 0;
else
accessible = false;
}
if (!accessible) {
// no access to c2; lose all access.
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A7]
}
if (!sameClass) {
changed |= ORIGINAL; // [A2]
}
if (m2 != m1 && m0 != m1) {
// hop to a third module; lose all access
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8]
}
if (!sameModule) {
changed |= MODULE; // [A3]
}
if (!samePackage) {
// Different package; loose PACKAGE and lower access.
changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4]
}
if (!sameTopLevel) {
// Different top-level class. Lose PRIVATE and PROTECTED access.
changed |= (PRIVATE|PROTECTED); // [A5]
}
if (sameClass) {
assertEquals(0, changed); // [A11] (no deprivation if same class)
}
if (accessible) assertEquals(0, changed & PUBLIC);
int modes2 = modes1 & ~changed;
Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]
if ((modes1 & UNCONDITIONAL) != 0) plc = null; // [A8]
LookupCase l2 = new LookupCase(c2, plc, modes2);
assertSame(l2.lookupClass(), c2); // [A1]
assertEquals(modes1, modes1 | modes2); // [A1-a] (no elevation of access)
assertTrue(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
return l2;
}
LookupCase dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
int newModes = oldModes & ~(modeToDrop | PROTECTED | ORIGINAL);
switch (modeToDrop) {
case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;
case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
case PRIVATE:
case ORIGINAL:
case UNCONDITIONAL: break;
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}
if (newModes == oldModes) return this; // return self if no change
LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);
assertEquals(oldModes, oldModes | newModes); // [A2] (no elevation of access)
assertTrue(l2.prevLookupClass() == null || (newModes & MODULE) == 0);
return l2;
}
boolean isModuleAccessible(Class<?> c) {
Module m1 = lookupClass().getModule();
Module m2 = c.getModule();
Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;
String pn = c.getPackageName();
boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);
if (m1 != m0) {
accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);
}
return accessible;
}
@Override
public String toString() {
String s = lookupClass().getSimpleName();
String lstr = lookupString();
int sl = lstr.indexOf('/');
if (sl >= 0) s += lstr.substring(sl);
ClassLoader cld = lookupClass().getClassLoader();
if (cld != THIS_LOADER) s += "/loader#"+numberOf(cld);
return s;
}
/** Predict the success or failure of accessing this method. */
public boolean willAccess(Method m) {
Class<?> c1 = lookupClass();
Class<?> c2 = m.getDeclaringClass();
Module m1 = c1.getModule();
Module m2 = c2.getModule();
Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
// unconditional has access to all public types/members of types that is in a package
// are unconditionally exported
if ((lookupModes & UNCONDITIONAL) != 0) {
return m2.isExported(c2.getPackageName())
&& Modifier.isPublic(c2.getModifiers())
&& Modifier.isPublic(m.getModifiers());
}
// c1 and c2 are in different module
if (m1 != m2 || m0 != m2) {
return (lookupModes & PUBLIC) != 0
&& isModuleAccessible(c2)
&& Modifier.isPublic(c2.getModifiers())
&& Modifier.isPublic(m.getModifiers());
}
assertNull(prevLookupClass);
if (!willAccessClass(c2, false))
return false;
LookupCase lc = this.in(c2);
int modes1 = lc.lookupModes();
int modes2 = fixMods(m.getModifiers());
// allow private lookup on nestmates. Otherwise, privacy is strictly enforced
if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
modes1 &= ~PRIVATE;
}
// protected access is sometimes allowed
if ((modes2 & PROTECTED) != 0) {
int prev = modes2;
modes2 |= PACKAGE; // it acts like a package method also
if ((lookupModes() & PROTECTED) != 0 &&
c2.isAssignableFrom(c1))
modes2 |= PUBLIC; // from a subclass, it acts like a public method also
}
if (verbosity >= 2)
System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0);
return (modes2 & modes1) != 0;
}
/** Predict the success or failure of accessing this class. */
public boolean willAccessClass(Class<?> c2, boolean load) {
Class<?> c1 = lookupClass();
if (load && c2.getClassLoader() != null) {
if (c1.getClassLoader() == null) {
// not visible
return false;
}
}
Module m1 = c1.getModule();
Module m2 = c2.getModule();
Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
// unconditional has access to all public types that is in an unconditionally exported package
if ((lookupModes & UNCONDITIONAL) != 0) {
return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());
}
// c1 and c2 are in different module
if (m1 != m2 || m0 != m2) {
return (lookupModes & PUBLIC) != 0
&& isModuleAccessible(c2)
&& Modifier.isPublic(c2.getModifiers());
}
assertNull(prevLookupClass);
LookupCase lc = this.in(c2);
int modes1 = lc.lookupModes();
boolean r = false;
if (modes1 == 0) {
r = false;
} else {
if (Modifier.isPublic(c2.getModifiers())) {
if ((modes1 & MODULE) != 0)
r = true;
else if ((modes1 & PUBLIC) != 0)
r = m1.isExported(c2.getPackageName());
} else {
if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
r = true;
}
}
if (verbosity >= 2) {
System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
}
return r;
}
}
private static Class<?> topLevelClass(Class<?> cls) {
Class<?> c = cls;
for (Class<?> ec; (ec = c.getEnclosingClass()) != null; )
c = ec;
assertNull(c.getEnclosingClass());
assertTrue(c == cls || cls.getEnclosingClass() != null);
return c;
}
private final TreeSet<LookupCase> CASES = new TreeSet<>();
private final TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>();
private final ArrayList<ClassLoader> LOADERS = new ArrayList<>();
private final ClassLoader THIS_LOADER = this.getClass().getClassLoader();
{ if (THIS_LOADER != null) LOADERS.add(THIS_LOADER); } // #1
private LookupCase lookupCase(String name) {
for (LookupCase lc : CASES) {
if (lc.toString().equals(name))
return lc;
}
throw new AssertionError(name);
}
private int numberOf(ClassLoader cl) {
if (cl == null) return 0;
int i = LOADERS.indexOf(cl);
if (i < 0) {
i = LOADERS.size();
LOADERS.add(cl);
}
return i+1;
}
private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {
TreeSet<LookupCase> edges = CASE_EDGES.get(l2);
if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>());
if (edges.add(l1)) {
Class<?> c1 = l1.lookupClass();
assertSame(l2.lookupClass(), c2); // [A1]
int m1 = l1.lookupModes();
int m2 = l2.lookupModes();
assertEquals(m1, (m1 | m2)); // [A2] (no elevation of access)
LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess);
if (!expect.equals(l2))
System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
assertEquals(expect, l2);
}
}
private void makeCases(Lookup[] originalLookups) {
// make initial set of lookup test cases
CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();
ArrayList<Class<?>> classes = new ArrayList<>();
for (Lookup l : originalLookups) {
CASES.add(new LookupCase(l));
classes.remove(l.lookupClass()); // no dups please
classes.add(l.lookupClass());
}
System.out.println("loaders = "+LOADERS);
int rounds = 0;
for (int lastCount = -1; lastCount != CASES.size(); ) {
lastCount = CASES.size(); // if CASES grow in the loop we go round again
for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {
for (int mode : ACCESS_CASES) {
LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));
addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);
CASES.add(lc2);
}
for (Class<?> c2 : classes) {
LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
addLookupEdge(lc1, c2, lc2, 0);
CASES.add(lc2);
}
}
rounds++;
}
System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds");
if (false) {
System.out.println("CASES: {");
for (LookupCase lc : CASES) {
System.out.println(lc);
Set<LookupCase> edges = CASE_EDGES.get(lc);
if (edges != null)
for (LookupCase prev : edges) {
System.out.println("\t"+prev);
}
}
System.out.println("}");
}
}
@Test public void test() {
makeCases(lookups());
if (verbosity > 0) {
verbosity += 9;
Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
testOneAccess(lookupCase("AccessControlTest/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");
verbosity -= 9;
}
Set<Class<?>> targetClassesDone = new HashSet<>();
for (LookupCase targetCase : CASES) {
Class<?> targetClass = targetCase.lookupClass();
if (!targetClassesDone.add(targetClass)) continue; // already saw this one
String targetPlace = placeName(targetClass);
if (targetPlace == null) continue; // Object, String, not a target
for (int targetAccess : ACCESS_CASES) {
if (targetAccess == MODULE || targetAccess == UNCONDITIONAL)
continue;
MethodType methodType = methodType(void.class);
Method method = targetMethod(targetClass, targetAccess, methodType);
// Try to access target method from various contexts.
for (LookupCase sourceCase : CASES) {
testOneAccess(sourceCase, method, "findClass");
testOneAccess(sourceCase, method, "accessClass");
testOneAccess(sourceCase, method, "find");
testOneAccess(sourceCase, method, "unreflect");
}
}
}
System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");
}
private int testCount, testCountFails;
private void testOneAccess(LookupCase sourceCase, Method method, String kind) {
Class<?> targetClass = method.getDeclaringClass();
String methodName = method.getName();
MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());
boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);
boolean willAccess = isFindOrAccessClass ?
sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);
boolean didAccess = false;
ReflectiveOperationException accessError = null;
try {
switch (kind) {
case "accessClass":
sourceCase.lookup().accessClass(targetClass);
break;
case "findClass":
sourceCase.lookup().findClass(targetClass.getName());
break;
case "find":
if ((method.getModifiers() & Modifier.STATIC) != 0)
sourceCase.lookup().findStatic(targetClass, methodName, methodType);
else
sourceCase.lookup().findVirtual(targetClass, methodName, methodType);
break;
case "unreflect":
sourceCase.lookup().unreflect(method);
break;
default:
throw new AssertionError(kind);
}
didAccess = true;
} catch (ReflectiveOperationException ex) {
accessError = ex;
}
if (willAccess != didAccess) {
System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));
System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);
assertEquals(didAccess, willAccess);
}
testCount++;
if (!didAccess) testCountFails++;
}
static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
String methodName = accessName(targetAccess)+placeName(targetClass);
if (verbosity >= 2)
System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
try {
Method method = targetClass.getDeclaredMethod(methodName, methodType.parameterArray());
assertEquals(methodType.returnType(), method.getReturnType());
int haveMods = method.getModifiers();
assertTrue(Modifier.isStatic(haveMods));
assertEquals(targetAccess, fixMods(haveMods));
return method;
} catch (NoSuchMethodException ex) {
throw new AssertionError(methodName, ex);
}
}
static String placeName(Class<?> cls) {
// return "self", "sibling", "nestmate", etc.
if (cls == AccessControlTest.class) return "self";
String cln = cls.getSimpleName();
int under = cln.lastIndexOf('_');
if (under < 0) return null;
return cln.substring(under+1);
}
static String accessName(int acc) {
switch (acc) {
case PUBLIC: return "pub_in_";
case PROTECTED: return "pro_in_";
case PACKAGE: return "pkg_in_";
case PRIVATE: return "pri_in_";
}
fail();
return "?";
}
private static final int[] ACCESS_CASES = {
PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL
};
/*
* Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
* Adjust 0 => PACKAGE
*/
/** Return one of the ACCESS_CASES. */
static int fixMods(int mods) {
mods &= (PUBLIC|PRIVATE|PROTECTED);
switch (mods) {
case PUBLIC: case PRIVATE: case PROTECTED: return mods;
case 0: return PACKAGE;
}
throw new AssertionError(mods);
}
static Lookup[] lookups() {
ArrayList<Lookup> tem = new ArrayList<>();
Collections.addAll(tem,
AccessControlTest.lookup_in_self(),
Inner_nestmate.lookup_in_nestmate(),
AccessControlTest_sibling.lookup_in_sibling());
if (true) {
Collections.addAll(tem,Acquaintance_remote.lookups());
} else {
try {
Class<?> remc = Class.forName("test.java.lang.invoke.AccessControlTest_subpkg.Acquaintance_remote");
Lookup[] remls = (Lookup[]) remc.getMethod("lookups").invoke(null);
Collections.addAll(tem, remls);
} catch (ReflectiveOperationException ex) {
throw new LinkageError("reflection failed", ex);
}
}
tem.add(publicLookup());
tem.add(publicLookup().in(String.class));
tem.add(publicLookup().in(List.class));
return tem.toArray(new Lookup[0]);
}
static Lookup lookup_in_self() {
return MethodHandles.lookup();
}
public static void pub_in_self() { }
protected static void pro_in_self() { }
static /*package*/ void pkg_in_self() { }
private static void pri_in_self() { }
static class Inner_nestmate {
static Lookup lookup_in_nestmate() {
return MethodHandles.lookup();
}
public static void pub_in_nestmate() { }
protected static void pro_in_nestmate() { }
static /*package*/ void pkg_in_nestmate() { }
private static void pri_in_nestmate() { }
}
}
class AccessControlTest_sibling {
static Lookup lookup_in_sibling() {
return MethodHandles.lookup();
}
public static void pub_in_sibling() { }
protected static void pro_in_sibling() { }
static /*package*/ void pkg_in_sibling() { }
private static void pri_in_sibling() { }
}
// This guy tests access from outside the package:
/*
package test.java.lang.invoke.AccessControlTest_subpkg;
public class Acquaintance_remote {
public static Lookup[] lookups() { ...
}
...
}
*/

View file

@ -0,0 +1,42 @@
package test.java.lang.invoke.AccessControlTest_subpkg;
import test.java.lang.invoke.AccessControlTest;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
// This guy tests access from outside the package test.java.lang.invoke:
public class Acquaintance_remote {
public static Lookup[] lookups() {
return new Lookup[] {
Acquaintance_remote.lookup_in_remote(),
Remote_subclass.lookup_in_subclass(),
Remote_hidden.lookup_in_hidden()
};
}
public static Lookup lookup_in_remote() {
return MethodHandles.lookup();
}
public static void pub_in_remote() { }
protected static void pro_in_remote() { }
static /*package*/ void pkg_in_remote() { }
private static void pri_in_remote() { }
public static class Remote_subclass extends AccessControlTest {
static Lookup lookup_in_subclass() {
return MethodHandles.lookup();
}
public static void pub_in_subclass() { }
protected static void pro_in_subclass() { }
static /*package*/ void pkg_in_subclass() { }
private static void pri_in_subclass() { }
}
static /*package*/ class Remote_hidden {
static Lookup lookup_in_hidden() {
return MethodHandles.lookup();
}
public static void pub_in_hidden() { }
protected static void pro_in_hidden() { }
static /*package*/ void pkg_in_hidden() { }
private static void pri_in_hidden() { }
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016, 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 8155106
* @run junit/othervm -ea -esa test.java.lang.invoke.ArrayConstructorTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import static java.lang.invoke.MethodType.methodType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class ArrayConstructorTest {
static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
@Test
public void testFindConstructorArray() {
var nsme = assertThrows(NoSuchMethodException.class, () -> LOOKUP.findConstructor(Object[].class, methodType(void.class)));
assertEquals("no constructor for array class: [Ljava.lang.Object;", nsme.getMessage());
}
static Object[][] arrayConstructorNegative() {
return new Object[][]{
{String.class, IllegalArgumentException.class, "not an array class: java.lang.String"},
{null, NullPointerException.class, null}
};
}
@ParameterizedTest
@MethodSource("arrayConstructorNegative")
public void testArrayConstructorNegative(Class<?> clazz, Class<? extends Exception> exceptionClass, String message) {
var e = assertThrowsExactly(exceptionClass, () -> MethodHandles.arrayConstructor(clazz));
if (message != null) {
assertEquals(message, e.getMessage());
}
}
@Test
public void testArrayConstructor() throws Throwable {
MethodHandle h = MethodHandles.arrayConstructor(String[].class);
assertEquals(methodType(String[].class, int.class), h.type());
String[] a = (String[]) h.invoke(17);
assertEquals(17, a.length);
}
@Test
public void testArrayConstructorNegativeIndex() throws Throwable {
MethodHandle h = MethodHandles.arrayConstructor(String[].class);
assertEquals(methodType(String[].class, int.class), h.type());
assertThrows(NegativeArraySizeException.class, () -> h.invoke(-1));
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2016, 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
* @run junit/othervm -ea -esa test.java.lang.invoke.ArrayLengthTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ArrayLengthTest {
static Object[] arrayClasses() {
return new Object[] {
int[].class,
long[].class,
float[].class,
double[].class,
boolean[].class,
byte[].class,
short[].class,
char[].class,
Object[].class,
StringBuffer[].class
};
}
@ParameterizedTest
@MethodSource("arrayClasses")
public void testArrayLength(Class<?> arrayClass) throws Throwable {
MethodHandle arrayLength = MethodHandles.arrayLength(arrayClass);
assertEquals(int.class, arrayLength.type().returnType());
assertEquals(arrayClass, arrayLength.type().parameterType(0));
Object array = MethodHandles.arrayConstructor(arrayClass).invoke(10);
assertEquals(10, arrayLength.invoke(array));
}
@ParameterizedTest
@MethodSource("arrayClasses")
public void testArrayLengthInvokeNPE(Class<?> arrayClass) throws Throwable {
MethodHandle arrayLength = MethodHandles.arrayLength(arrayClass);
assertThrows(NullPointerException.class, () -> arrayLength.invoke(null));
}
@Test
public void testArrayLengthNoArray() {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.arrayLength(String.class));
}
@Test
public void testArrayLengthNPE() {
assertThrows(NullPointerException.class, () -> MethodHandles.arrayLength(null));
}
@Test
public void testNullReference() throws Throwable {
MethodHandle arrayLength = MethodHandles.arrayLength(String[].class);
assertThrows(NullPointerException.class, () -> {
int len = (int)arrayLength.invokeExact((String[])null);
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2011, 2012, 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 smoke tests for CallSite
* @library /test/lib
*
* @build indify.Indify
* @compile CallSiteTest.java
* @run main/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -Xbatch
* indify.Indify
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.CallSiteTest
*/
package test.java.lang.invoke;
import java.io.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import static jdk.test.lib.Asserts.*;
public class CallSiteTest {
private static final Class<?> CLASS = CallSiteTest.class;
private static CallSite mcs;
private static CallSite vcs;
private static MethodHandle mh_foo;
private static MethodHandle mh_bar;
static {
try {
mh_foo = lookup().findStatic(CLASS, "foo", methodType(int.class, int.class, int.class));
mh_bar = lookup().findStatic(CLASS, "bar", methodType(int.class, int.class, int.class));
mcs = new MutableCallSite(mh_foo);
vcs = new VolatileCallSite(mh_foo);
} catch (Exception e) {
e.printStackTrace();
throw new Error(e);
}
}
public static void main(String... av) throws Throwable {
testConstantCallSite();
testMutableCallSite();
testVolatileCallSite();
}
private static final int N = Integer.MAX_VALUE / 100;
private static final int RESULT1 = 762786192;
private static final int RESULT2 = -21474836;
static final CallSite MCS = new MutableCallSite(methodType(void.class));
static final MethodHandle MCS_INVOKER = MCS.dynamicInvoker();
static void test(boolean shouldThrow) {
try {
MCS_INVOKER.invokeExact();
if (shouldThrow) {
throw new AssertionError("should throw");
}
} catch (IllegalStateException ise) {
if (!shouldThrow) {
throw new AssertionError("should not throw", ise);
}
} catch (Throwable e) {
throw new Error(e);
}
}
static class MyCCS extends ConstantCallSite {
public MyCCS(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
super(targetType, createTargetHook);
}
}
private static MethodHandle testConstantCallSiteHandler(CallSite cs, CallSite[] holder) throws Throwable {
holder[0] = cs; // capture call site instance for subsequent checks
MethodType csType = cs.type(); // should not throw on partially constructed instance
// Truly dynamic invoker for constant call site
MethodHandle getTarget = lookup().findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class))
.bindTo(cs);
MethodHandle invoker = MethodHandles.exactInvoker(csType);
MethodHandle target = MethodHandles.foldArguments(invoker, getTarget);
MCS.setTarget(target);
// warmup
for (int i = 0; i < 20_000; i++) {
test(true); // should throw IllegalStateException
}
return MethodHandles.empty(csType); // initialize cs with an empty method handle
}
private static void testConstantCallSite() throws Throwable {
CallSite[] holder = new CallSite[1];
MethodHandle handler = lookup().findStatic(CLASS, "testConstantCallSiteHandler", MethodType.methodType(MethodHandle.class, CallSite.class, CallSite[].class));
handler = MethodHandles.insertArguments(handler, 1, new Object[] { holder } );
CallSite ccs = new MyCCS(MCS.type(), handler); // trigger call to handler
if (ccs != holder[0]) {
throw new AssertionError("different call site instances");
}
for (int i = 0; i < 20_000; i++) {
test(false); // should not throw
}
}
private static void testMutableCallSite() throws Throwable {
// warm-up
for (int i = 0; i < 20000; i++) {
mcs.setTarget(mh_foo);
}
// run
for (int n = 0; n < 2; n++) {
mcs.setTarget(mh_foo);
for (int i = 0; i < 5; i++) {
assertEQ(RESULT1, runMutableCallSite());
}
mcs.setTarget(mh_bar);
for (int i = 0; i < 5; i++) {
assertEQ(RESULT2, runMutableCallSite());
}
}
}
private static void testVolatileCallSite() throws Throwable {
// warm-up
for (int i = 0; i < 20000; i++) {
vcs.setTarget(mh_foo);
}
// run
for (int n = 0; n < 2; n++) {
vcs.setTarget(mh_foo);
for (int i = 0; i < 5; i++) {
assertEQ(RESULT1, runVolatileCallSite());
}
vcs.setTarget(mh_bar);
for (int i = 0; i < 5; i++) {
assertEQ(RESULT2, runVolatileCallSite());
}
}
}
private static int runMutableCallSite() throws Throwable {
int sum = 0;
for (int i = 0; i < N; i++) {
sum += (int) INDY_mcs().invokeExact(i, i+1);
}
return sum;
}
private static int runVolatileCallSite() throws Throwable {
int sum = 0;
for (int i = 0; i < N; i++) {
sum += (int) INDY_vcs().invokeExact(i, i+1);
}
return sum;
}
static int foo(int a, int b) { return a + b; }
static int bar(int a, int b) { return a - b; }
private static MethodType MT_bsm() {
shouldNotCallThis();
return methodType(CallSite.class, Lookup.class, String.class, MethodType.class);
}
private static CallSite bsm_mcs(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
return mcs;
}
private static MethodHandle MH_bsm_mcs() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(), "bsm_mcs", MT_bsm());
}
private static MethodHandle INDY_mcs() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm_mcs().invoke(lookup(), "foo", methodType(int.class, int.class, int.class))).dynamicInvoker();
}
private static CallSite bsm_vcs(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
return vcs;
}
private static MethodHandle MH_bsm_vcs() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(), "bsm_vcs", MT_bsm());
}
private static MethodHandle INDY_vcs() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm_vcs().invoke(lookup(), "foo", methodType(int.class, int.class, int.class))).dynamicInvoker();
}
private static void shouldNotCallThis() {
// if this gets called, the transformation has not taken place
throw new AssertionError("this code should be statically transformed away by Indify");
}
}

View file

@ -0,0 +1,274 @@
/*
* Copyright (c) 2013, 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 static initializer invocation order
*
* @build indify.Indify
* @compile CallStaticInitOrder.java
* @run main/othervm
* indify.Indify
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.CallStaticInitOrder
*/
package test.java.lang.invoke;
import java.io.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
public class CallStaticInitOrder {
private static int TICK;
private static synchronized int tick(String event) {
int n = ++TICK;
System.out.println("event #"+n+" = "+event);
return n;
}
static int Init1Tick;
private static class Init1 {
static { Init1Tick = tick("foo -> Init1.<clinit>"); }
static int foo() { return Init1Tick; }
}
static int Init2Tick;
private static class Init2 {
static { Init2Tick = tick("bar -> Init2.<clinit>"); }
static int bar() { return Init2Tick; }
}
static int Init3Tick;
private static class Init3 {
static { Init3Tick = tick("baz -> Init3.<clinit>"); }
static int baz() { return Init3Tick; }
}
static int Init4Tick;
private static class Init4 {
static { Init4Tick = tick("bat -> Init4.<clinit>"); }
static int bat() { return Init4Tick; }
}
static int Init5Tick;
private static class Init5 {
static { Init5Tick = tick("read bang -> Init5.<clinit>"); }
static int bang = Init5Tick;
}
static int Init6Tick;
private static class Init6 {
static { Init6Tick = tick("write pong -> Init6.<clinit>"); }
static int pong;
}
private static final MutableCallSite CONSTANT_CS_baz;
private static MethodHandle MH_foo() throws ReflectiveOperationException {
return lookup().findStatic(Init1.class, "foo", methodType(int.class));
}
private static final MethodHandle CONSTANT_MH_bar;
private static MethodHandle MH_baz() throws ReflectiveOperationException {
return lookup().findStatic(Init3.class, "baz", methodType(int.class));
}
private static final MethodHandle CONSTANT_MH_bat;
private static final MethodHandle CONSTANT_MH_bangGetter;
private static final MethodHandle CONSTANT_MH_pongSetter;
static {
try {
int t1 = tick("CallStaticInitOrder.<clinit>");
{
CONSTANT_CS_baz = new MutableCallSite(methodType(int.class));
// MH_foo() := lookup().findStatic(Init1.class, "foo", methodType(int.class));
CONSTANT_MH_bar = lookup().findStatic(Init2.class, "bar", methodType(int.class));
// MH_baz() := lookup().findStatic(Init3.class, "baz", methodType(int.class));
CONSTANT_MH_bat = lookup().unreflect(Init4.class.getDeclaredMethod("bat"));
CONSTANT_MH_bangGetter = lookup().findStaticGetter(Init5.class, "bang", int.class);
MethodHandle pongSetter = lookup().findStaticSetter(Init6.class, "pong", int.class);
MethodHandle tickGetter = lookup().findStaticGetter(CallStaticInitOrder.class, "Init6Tick", int.class);
CONSTANT_MH_pongSetter = filterReturnValue(insertArguments(pongSetter, 0, -99), tickGetter);
}
int t2 = tick("CallStaticInitOrder.<clinit> done");
assertEquals(t1+1, t2); // no ticks in between
} catch (Exception ex) {
throw new InternalError(ex.toString());
}
}
public static void main(String... av) throws Throwable {
testInit();
if (LAST_LOSER != null) throw LAST_LOSER;
}
private static Throwable LAST_LOSER;
private static void assertEquals(int expected, int actual) {
if (expected != actual) {
Throwable loser = new AssertionError("expected: " + expected + ", actual: " + actual);
if (LAST_LOSER != null)
LAST_LOSER.printStackTrace(System.out);
LAST_LOSER = loser;
}
}
private static void testInit() throws Throwable {
System.out.println("runFoo = "+runFoo());
System.out.println("runBar = "+runBar());
try {
runBaz();
} catch (IllegalStateException ex) {
tick("runBaz throw/catch");
}
CONSTANT_CS_baz.setTarget(MH_baz());
System.out.println("runBaz = "+runBaz());
System.out.println("runBat = "+runBat());
System.out.println("runBang = "+runBang());
System.out.println("runPong = "+runPong());
}
private static int runFoo() throws Throwable {
assertEquals(Init1Tick, 0); // Init1 not initialized yet
int t1 = tick("runFoo");
int t2 = (int) INDY_foo().invokeExact();
int t3 = tick("runFoo done");
assertEquals(Init1Tick, t2); // when Init1 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_foo() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "foo", methodType(int.class))).dynamicInvoker();
}
private static int runBar() throws Throwable {
assertEquals(Init2Tick, 0); // Init2 not initialized yet
int t1 = tick("runBar");
int t2 = (int) INDY_bar().invokeExact();
int t3 = tick("runBar done");
assertEquals(Init2Tick, t2); // when Init2 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_bar() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "bar", methodType(int.class))).dynamicInvoker();
}
private static int runBaz() throws Throwable {
assertEquals(Init3Tick, 0); // Init3 not initialized yet
int t1 = tick("runBaz");
int t2 = (int) INDY_baz().invokeExact();
int t3 = tick("runBaz done");
assertEquals(Init3Tick, t2); // when Init3 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_baz() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "baz", methodType(int.class))).dynamicInvoker();
}
private static int runBat() throws Throwable {
assertEquals(Init4Tick, 0); // Init4 not initialized yet
int t1 = tick("runBat");
int t2 = (int) INDY_bat().invokeExact();
int t3 = tick("runBat done");
assertEquals(Init4Tick, t2); // when Init4 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_bat() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "bat", methodType(int.class))).dynamicInvoker();
}
private static int runBang() throws Throwable {
assertEquals(Init5Tick, 0); // Init5 not initialized yet
int t1 = tick("runBang");
int t2 = (int) INDY_bang().invokeExact();
int t3 = tick("runBang done");
assertEquals(Init5Tick, t2); // when Init5 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_bang() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "bang", methodType(int.class))).dynamicInvoker();
}
private static int runPong() throws Throwable {
assertEquals(Init6Tick, 0); // Init6 not initialized yet
int t1 = tick("runPong");
int t2 = (int) INDY_pong().invokeExact();
int t3 = tick("runPong done");
assertEquals(Init6Tick, t2); // when Init6 was initialized
assertEquals(t1+2, t3); // exactly two ticks in between
assertEquals(t1+1, t2); // init happened inside
return t2;
}
private static MethodHandle INDY_pong() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(), "pong", methodType(int.class))).dynamicInvoker();
}
private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
System.out.println("bsm "+name+type);
CallSite res;
switch (name) {
case "foo":
res = new ConstantCallSite(MH_foo()); break;
case "bar":
res = new ConstantCallSite(CONSTANT_MH_bar); break;
case "baz":
res = CONSTANT_CS_baz; break;
case "bat":
res = new ConstantCallSite(CONSTANT_MH_bat); break;
case "bang":
res = new ConstantCallSite(CONSTANT_MH_bangGetter); break;
case "pong":
res = new ConstantCallSite(CONSTANT_MH_pongSetter); break;
default:
res = null;
}
if (res == null || !res.type().equals(type)) {
throw new AssertionError(String.valueOf(res));
}
return res;
}
private static MethodHandle MH_bsm() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(), "bsm",
methodType(CallSite.class, Lookup.class, String.class, MethodType.class));
}
private static void shouldNotCallThis() {
// if this gets called, the transformation has not taken place
throw new AssertionError("this code should be statically transformed away by Indify");
}
}

View file

@ -0,0 +1,59 @@
/*
* 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
* @run junit/othervm CallerSensitiveMethodHandle
* @summary Check Lookup findVirtual, findStatic and unreflect behavior with
* caller sensitive methods with focus on AccessibleObject.setAccessible
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import static java.lang.invoke.MethodType.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class CallerSensitiveMethodHandle {
private static int field = 0;
@Test
public void privateField() throws Throwable {
Lookup l = MethodHandles.lookup();
Field f = CallerSensitiveMethodHandle.class.getDeclaredField("field");
MethodHandle mh = l.findVirtual(Field.class, "setInt", methodType(void.class, Object.class, int.class));
int newValue = 5;
mh.invokeExact(f, (Object) null, newValue);
assertEquals(newValue, field);
}
@Test
public void lookupItself() throws Throwable {
Lookup lookup = MethodHandles.lookup();
MethodHandle MH_lookup2 = lookup.findStatic(MethodHandles.class, "lookup", methodType(Lookup.class));
Lookup lookup2 = (Lookup) MH_lookup2.invokeExact();
System.out.println(lookup2 + " original lookup class " + lookup.lookupClass());
assertSame(lookup.lookupClass(), lookup2.lookupClass());
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2017, 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
* @summary Smoke-test class specializer, used to create BoundMethodHandle classes
* @compile/module=java.base java/lang/invoke/ClassSpecializerHelper.java
* @run junit/othervm/timeout=250 -ea -esa ClassSpecializerTest
*/
// Useful diagnostics to try:
// -Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true
// -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true
import java.lang.invoke.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static java.lang.invoke.ClassSpecializerHelper.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
public class ClassSpecializerTest {
@Test
public void testFindSpecies() throws Throwable {
System.out.println("testFindSpecies");
System.out.println("test = " + SPEC_TEST);
ArrayList<Object> args = new ArrayList<>();
for (int key = 0; key <= Kind.MAX_KEY; key++) {
Kind k = SpecTest.kind(key);
System.out.println("k = " + k);
MethodHandle mh = k.factory();
System.out.println("k.f = " + mh);
args.clear();
for (Class<?> pt : mh.type().parameterList()) {
args.add(coughUpA(pt));
}
args.set(0, key * 1000 + 42);
Frob f = (Frob) mh.invokeWithArguments(args.toArray());
assertSame(k, f.kind());
System.out.println("k.f(...) = " + f.toString());
List<Object> l = f.asList();
System.out.println("f.l = " + l);
args.subList(0,1).clear(); // drop label
assertEquals(args, l);
}
}
private static Object coughUpA(Class<?> pt) throws Throwable {
if (pt == String.class) return "foo";
if (pt.isArray()) return java.lang.reflect.Array.newInstance(pt.getComponentType(), 2);
if (pt == Integer.class) return 42;
if (pt == Double.class) return 3.14;
if (pt.isAssignableFrom(List.class))
return Arrays.asList("hello", "world", "from", pt.getSimpleName());
return MethodHandles.zero(pt).invoke();
}
public static void main(String... av) throws Throwable {
System.out.println("TEST: ClassSpecializerTest");
new ClassSpecializerTest().testFindSpecies();
}
}

View file

@ -0,0 +1,543 @@
/*
* Copyright (c) 2011, 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 8351045 8351996 8358535
* @summary tests for class-specific values
* @modules java.base/java.lang:+open
* @library /test/lib
* @run junit ClassValueTest
*/
import java.lang.classfile.ClassFile;
import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.ref.WeakReference;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import jdk.test.lib.util.ForceGC;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author jrose
*/
final class ClassValueTest {
static String nameForCV1(Class<?> type) {
return "CV1:" + type.getName();
}
int countForCV1;
final ClassValue<String> CV1 = new CV1();
private class CV1 extends ClassValue<String> {
protected String computeValue(Class<?> type) {
countForCV1++;
return nameForCV1(type);
}
}
static final Class<?>[] CLASSES = {
String.class,
Integer.class,
int.class,
boolean[].class,
char[][].class,
ClassValueTest.class
};
@Test
public void testGet() {
countForCV1 = 0;
for (Class<?> c : CLASSES) {
assertEquals(nameForCV1(c), CV1.get(c));
}
assertEquals(CLASSES.length, countForCV1);
for (Class<?> c : CLASSES) {
assertEquals(nameForCV1(c), CV1.get(c));
}
assertEquals(CLASSES.length, countForCV1);
}
@Test
public void testRemove() {
for (Class<?> c : CLASSES) {
CV1.get(c);
}
countForCV1 = 0;
int REMCOUNT = 3;
for (int i = 0; i < REMCOUNT; i++) {
CV1.remove(CLASSES[i]);
}
assertEquals(0, countForCV1); // no change
for (Class<?> c : CLASSES) {
assertEquals(nameForCV1(c), CV1.get(c));
}
assertEquals(REMCOUNT, countForCV1);
}
static String nameForCVN(Class<?> type, int n) {
return "CV[" + n + "]" + type.getName();
}
int countForCVN;
class CVN extends ClassValue<String> {
final int n;
CVN(int n) { this.n = n; }
protected String computeValue(Class<?> type) {
countForCVN++;
return nameForCVN(type, n);
}
};
@Test
public void testGetMany() {
int CVN_COUNT1 = 100, CVN_COUNT2 = 100;
CVN cvns[] = new CVN[CVN_COUNT1 * CVN_COUNT2];
for (int n = 0; n < cvns.length; n++) {
cvns[n] = new CVN(n);
}
countForCVN = 0;
for (int pass = 0; pass <= 2; pass++) {
for (int i1 = 0; i1 < CVN_COUNT1; i1++) {
eachClass:
for (Class<?> c : CLASSES) {
for (int i2 = 0; i2 < CVN_COUNT2; i2++) {
int n = i1*CVN_COUNT2 + i2;
assertEquals(0, countForCVN);
assertEquals(nameForCVN(c, n), cvns[n].get(c));
cvns[n].get(c); //get it again
//System.out.println("getting "+n+":"+cvns[n].get(c));
boolean doremove = (((i1 + i2) & 3) == 0);
switch (pass) {
case 0:
assertEquals(1, countForCVN);
break;
case 1:
// remove on middle pass
assertEquals(0, countForCVN);
if (doremove) {
//System.out.println("removing "+n+":"+cvns[n].get(c));
cvns[n].remove(c);
assertEquals(0, countForCVN);
}
break;
case 2:
assertEquals(doremove ? 1 : 0, countForCVN);
break;
}
countForCVN = 0;
if (i1 > i2 && i1 < i2+5) continue eachClass; // leave diagonal gap
}
}
}
}
assertEquals(0, countForCVN);
System.out.println("[rechecking values]");
for (int i = 0; i < cvns.length * 10; i++) {
int n = i % cvns.length;
for (Class<?> c : CLASSES) {
assertEquals(nameForCVN(c, n), cvns[n].get(c));
}
}
}
private static final long COMPUTE_TIME_MILLIS = 100;
// Adjust this timeout to fail faster for test stalls
private static final Duration TIMEOUT = Duration.ofNanos((long) (
Duration.of(1, ChronoUnit.MINUTES).toNanos()
* Double.parseDouble(System.getProperty("test.timeout.factor", "1.0"))));
private static void await(CountDownLatch latch) {
try {
if (!latch.await(TIMEOUT.toNanos(), TimeUnit.NANOSECONDS)) {
fail("No signal received");
}
} catch (InterruptedException e) {
fail(e);
}
}
private static void awaitThreads(Thread... threads) {
awaitThreads(Arrays.asList(threads));
}
private static void awaitThreads(Iterable<Thread> threads) {
for (var t : threads) {
try {
if (!t.join(TIMEOUT)) {
fail("Thread not stopping " + t);
}
} catch (InterruptedException ex) {
fail(ex);
}
}
}
/**
* Tests that get() + remove() can prevent stale value from being installed.
* Uses junit to do basic stress.
*/
@Test
void testRemoveStale() throws InterruptedException {
CountDownLatch oldInputUsed = new CountDownLatch(1);
CountDownLatch inputUpdated = new CountDownLatch(1);
AtomicInteger input = new AtomicInteger(0);
ClassValue<Integer> cv = new ClassValue<>() {
@Override
protected Integer computeValue(Class<?> type) {
// must get early to represent using outdated input
int v = input.get();
oldInputUsed.countDown();
// ensure input is updated when we return
await(inputUpdated);
return v;
}
};
var innocuous = Thread.startVirtualThread(() -> cv.get(int.class));
var refreshInput = Thread.startVirtualThread(() -> {
await(oldInputUsed);
input.incrementAndGet();
cv.remove(int.class); // Let's recompute with updated inputs!
inputUpdated.countDown();
});
awaitThreads(innocuous, refreshInput);
assertEquals(1, input.get(), "input not updated");
assertEquals(1, cv.get(int.class), "CV not using up-to-date input");
}
/**
* Tests that calling get() from computeValue() terminates.
*/
@Test
void testGetInCompute() {
ClassValue<Object> cv = new ClassValue<>() {
@Override
protected Object computeValue(Class<?> type) {
get(type);
get(type);
get(type);
return Boolean.TRUE;
}
};
try {
cv.get(int.class);
} catch (Throwable ex) {
// swallow if any
}
}
/**
* Tests that calling remove() from computeValue() terminates.
*/
@Test
void testRemoveInCompute() {
ClassValue<Boolean> cv = new ClassValue<>() {
@Override
protected Boolean computeValue(Class<?> type) {
remove(type);
remove(type);
remove(type);
return Boolean.TRUE;
}
};
assertTrue(cv.get(int.class));
}
private static Class<?> createWeakClass() {
var bytes = ClassFile.of().build(ClassDesc.of("WeakTest"), _ -> {});
try {
return MethodHandles.lookup().defineHiddenClass(bytes, true).lookupClass();
} catch (IllegalAccessException ex) {
return fail(ex);
}
}
@Test
void testWeakAgainstClass() {
Class<?> hidden = createWeakClass();
ClassValue<int[]> cv = new ClassValue<>() {
@Override
protected int[] computeValue(Class<?> type) {
return new int[23];
}
};
WeakReference<?> ref = new WeakReference<>(cv.get(hidden));
hidden = null; // Remove reference for interpreter
if (!ForceGC.wait(() -> ref.refersTo(null))) {
fail("Timeout");
}
}
@Test
@Disabled // JDK-8352622
void testWeakAgainstClassValue() {
ClassValue<int[]> cv = new ClassValue<>() {
@Override
protected int[] computeValue(Class<?> type) {
return new int[23];
}
};
WeakReference<?> ref = new WeakReference<>(cv.get(int.class));
cv = null; // Remove reference for interpreter
if (!ForceGC.wait(() -> {
CV1.get(int.class); // flush the weak maps
return ref.refersTo(null);
})) {
fail("Timeout");
}
}
@Test
void testSingletonWinner() {
CountDownLatch raceStart = new CountDownLatch(1);
ClassValue<int[]> cv = new ClassValue<>() {
@Override
protected int[] computeValue(Class<?> type) {
await(raceStart);
return new int[] {ThreadLocalRandom.current().nextInt()};
}
};
AtomicReference<int[]> truth = new AtomicReference<>(null);
AtomicInteger truthSwapCount = new AtomicInteger(0);
List<Thread> threads = new ArrayList<>(100);
Runnable job = () -> {
var res = cv.get(ClassValueTest.class);
var item = truth.compareAndExchange(null, res);
if (item != null) {
assertSame(item, res);
} else {
truthSwapCount.incrementAndGet();
}
};
for (int i = 0; i < 100; i++) {
threads.add(Thread.startVirtualThread(job));
}
raceStart.countDown();
awaitThreads(threads);
assertEquals(1, truthSwapCount.get());
}
@Test
void testRacyRemoveInCompute() {
ClassValue<Object> cv = new ClassValue<>() {
@Override
protected Object computeValue(Class<?> type) {
remove(type);
try {
Thread.sleep(COMPUTE_TIME_MILLIS);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
remove(type);
return Boolean.TRUE;
}
};
var threads = Arrays.stream(CLASSES)
.map(clz -> Thread.startVirtualThread(() -> cv.get(clz)))
.toList();
awaitThreads(threads);
}
@Test
void testRecursiveInitialization() {
record Holder() {
static final ClassValue<Object> clv = new ClassValue<>() {
@Override
protected Object computeValue(Class<?> type) {
return new One();
}
};
record One() {
static {
Holder.clv.get(One.class);
}
}
}
Holder.clv.get(Holder.One.class);
}
private static final ScopedValue<Integer> THREAD_ID = ScopedValue.newInstance();
@Test
void testNoRecomputeOnUnrelatedRemoval() throws InterruptedException {
CountDownLatch t1Started = new CountDownLatch(1);
CountDownLatch removeTamper = new CountDownLatch(1);
CountDownLatch t2Started = new CountDownLatch(1);
CountDownLatch t1Returned = new CountDownLatch(1);
AtomicInteger t1Tries = new AtomicInteger();
ClassValue<Object> clv = new ClassValue<>() {
@Override
protected Object computeValue(Class<?> type) {
int id = THREAD_ID.get();
if (id == 1) {
t1Tries.incrementAndGet();
t1Started.countDown();
await(t2Started); // implies unrelated changes
} else if (id == 2) {
t2Started.countDown();
// Don't race to install - we want to check computeValue tampers
await(t1Returned);
} else if (id != -1) {
fail("Id: " + id);
}
return "";
}
};
ScopedValue.where(THREAD_ID, -1).run(() -> clv.get(long.class)); // set up unrelated class
var t1 = Thread.startVirtualThread(() ->
ScopedValue.where(THREAD_ID, 1).run(() -> {
clv.get(int.class);
t1Returned.countDown(); // returned after x calls to computeValue
}));
var t2 = Thread.startVirtualThread(() ->
ScopedValue.where(THREAD_ID, 2).run(() -> {
await(removeTamper);
clv.get(int.class); // clv version diff from that of promise
}));
await(t1Started);
clv.remove(long.class);
removeTamper.countDown(); // removed unrelated class
awaitThreads(t1, t2);
assertEquals(1, t1Tries.get(), "Redundant computeValue retries");
}
@Test
void testNoObsoleteInstallation() throws InterruptedException {
CountDownLatch slowComputationStart = new CountDownLatch(1);
CountDownLatch slowComputationContinue = new CountDownLatch(1);
ClassValue<Integer> clv = new ClassValue<>() {
@Override
protected Integer computeValue(Class<?> type) {
if (!THREAD_ID.isBound()) {
return 5;
}
int threadId = THREAD_ID.get();
if (threadId == 1) {
if (slowComputationContinue.getCount() == 0) {
return 42;
} else {
// First invocation
slowComputationStart.countDown();
await(slowComputationContinue);
return -1;
}
} else {
return fail("Unknown thread " + threadId);
}
}
};
var t = Thread.startVirtualThread(() -> ScopedValue.where(THREAD_ID, 1).run(() -> {
int v = clv.get(int.class);
assertEquals(42, v, "recomputed value");
}));
await(slowComputationStart);
assertEquals(5, clv.get(int.class), "fast computation installed value");
clv.remove(int.class);
slowComputationContinue.countDown();
awaitThreads(t);
assertEquals(42, clv.get(int.class), "slow computation reinstalled value");
}
// ClassValue cache invalidated and not reinstated when another
// unrelated entry is removed
@Test
public void testCacheRefresh() throws Throwable {
// Setup
var lookup = MethodHandles.privateLookupIn(ClassValue.class, MethodHandles.lookup());
var classValueEntryClass = Class.forName("java.lang.ClassValue$Entry");
MethodHandle getCacheCarefully = lookup.findStatic(ClassValue.class, "getCacheCarefully",
MethodType.methodType(classValueEntryClass.arrayType(), Class.class));
var classValueMapClass = Class.forName("java.lang.ClassValue$ClassValueMap");
MethodHandle probeHomeLocation = lookup.findStatic(classValueMapClass, "probeHomeLocation",
MethodType.methodType(classValueEntryClass, classValueEntryClass.arrayType(), ClassValue.class));
MethodHandle match = lookup.findVirtual(ClassValue.class, "match",
MethodType.methodType(boolean.class, classValueEntryClass));
// Work
ClassValue<?> clv = new ClassValue<>() {
@Override
protected String computeValue(Class<?> type) {
return "";
}
};
// A class that shouldn't have arbitrary values stuffing the cache
var cleanClass = clv.getClass();
clv.get(cleanClass); // create cache on clean class
assertTrue(checkDirectCacheMatch(
getCacheCarefully,
probeHomeLocation,
match,
clv,
cleanClass
));
clv.get(int.class);
clv.remove(int.class); // invalidate cache on clean class
assertFalse(checkDirectCacheMatch(
getCacheCarefully,
probeHomeLocation,
match,
clv,
cleanClass
));
clv.get(cleanClass);
assertTrue(checkDirectCacheMatch(
getCacheCarefully,
probeHomeLocation,
match,
clv,
cleanClass
));
}
private static boolean checkDirectCacheMatch(
MethodHandle getCacheCarefully,
MethodHandle probeHomeLocation,
MethodHandle match,
ClassValue<?> clv,
Class<?> cl
) throws Throwable {
Object cache = getCacheCarefully.invoke(cl);
Object entry = probeHomeLocation.invoke(cache, clv);
return (boolean) match.invoke(clv, entry);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2015, 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 8143232
* @summary Test verifies that LF bootstraps properly when run with COMPILE_THRESHOLD set
* @compile CompileThresholdBootstrapTest.java
* @run junit/othervm -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=30 test.java.lang.invoke.CompileThresholdBootstrapTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandles;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public final class CompileThresholdBootstrapTest {
@Test
public void testBootstrap() throws Throwable {
assertEquals(0, (int)MethodHandles.constant(int.class, (int)0).invokeExact());
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2016, 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
* @summary unit tests for java.lang.invoke.MethodHandles
* @run junit/othervm -ea -esa test.java.lang.invoke.ConstantIdentityMHTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import static java.lang.invoke.MethodHandles.*;
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 ConstantIdentityMHTest {
private static Object[][] testZeroData() {
return new Object[][] {
{void.class, "()void"},
{int.class, "()int"},
{byte.class, "()byte"},
{short.class, "()short"},
{long.class, "()long"},
{float.class, "()float"},
{double.class, "()double"},
{boolean.class, "()boolean"},
{char.class, "()char"},
{Integer.class, "()Integer"}
};
}
@ParameterizedTest
@MethodSource("testZeroData")
public void testZero(Class<?> expectedtype, String expected) throws Throwable {
assertEquals(expected, MethodHandles.zero(expectedtype).type().toString());
}
@Test
public void testZeroNPE() {
assertThrows(NullPointerException.class, () -> MethodHandles.zero(null));
}
@Test
void testEmpty() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
assertEquals("xy", (String)cat.invoke("x","y"));
MethodHandle mhEmpty = MethodHandles.empty(cat.type());
assertNull((String) mhEmpty.invoke("x", "y"));
}
@Test
void testEmptyNPE() {
assertThrows(NullPointerException.class, () -> MethodHandles.empty(null));
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8164102
* @run main/othervm -ea -esa test.java.lang.invoke.CountedLoopIterationCountsTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class CountedLoopIterationCountsTest {
public static void main(String[] args) throws Throwable {
run(1, -10, 0);
run(1, 0, 0);
run(Integer.MAX_VALUE - 1, Integer.MIN_VALUE + 10, 0);
run(Integer.MIN_VALUE, Integer.MIN_VALUE + 4, 4);
run(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, 1);
run(Integer.MAX_VALUE - 1, 0, 0);
run(Integer.MAX_VALUE - 1, 10, 0);
run(Integer.MAX_VALUE - 1, -10, 0);
run(Integer.MAX_VALUE, Integer.MIN_VALUE + 10, 0);
run(Integer.MAX_VALUE - 1, Integer.MAX_VALUE, 1);
run(Integer.MAX_VALUE, Integer.MAX_VALUE, 0);
if (failed) {
throw new AssertionError("one or more tests failed");
}
}
static boolean failed = false;
private static void run(int start, int end, int expectedIterations) throws Throwable {
System.out.println("run from " + start + " to " + end);
MethodHandle loop = MethodHandles.countedLoop(
MethodHandles.constant(int.class, start), // iterate from given start (inclusive) ...
MethodHandles.constant(int.class, end), // ... to given end (exclusive)
MH_m1, // initialise loop variable to -1
MH_step); // increment loop counter by one in each iteration
// The loop variable's value, and hence the loop result, will be "number of iterations" minus one.
int r = (int) loop.invoke();
if (r + 1 != expectedIterations) {
System.out.println("expected " + expectedIterations + " iterations, but got " + r);
failed = true;
}
}
static int step(int stepCount, int counter) {
return stepCount + 1;
}
static final MethodHandle MH_m1;
static final MethodHandle MH_step;
static {
try {
MH_m1 = MethodHandles.constant(int.class, -1);
MH_step = MethodHandles.lookup().findStatic(CountedLoopIterationCountsTest.class, "step",
MethodType.methodType(int.class, int.class, int.class));
} catch (Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015, 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.MethodHandle;
import java.lang.invoke.MethodHandleHelper;
import java.lang.invoke.MethodType;
/* @test
* @summary Assertion in LambdaFormEditor.bindArgumentType is too strong
*
* @compile/module=java.base java/lang/invoke/MethodHandleHelper.java
* @run main/othervm -esa CustomizedLambdaFormTest
*/
public class CustomizedLambdaFormTest {
static void testExtendCustomizedBMH() throws Exception {
// Construct BMH
MethodHandle mh = MethodHandleHelper.IMPL_LOOKUP.findVirtual(String.class, "concat",
MethodType.methodType(String.class, String.class))
.bindTo("a");
MethodHandleHelper.customize(mh);
mh.bindTo("b"); // Try to extend customized BMH
}
public static void main(String[] args) throws Throwable {
testExtendCustomizedBMH();
}
}

View file

@ -0,0 +1,335 @@
/*
* Copyright (c) 2017, 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
* @modules java.base/java.lang:open
* @run junit/othervm test.DefineClassTest
* @summary Basic test for java.lang.invoke.MethodHandles.Lookup.defineClass
*/
package test;
import java.lang.classfile.ClassFile;
import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.AccessFlag;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
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.CLASS_INIT_NAME;
import static java.lang.constant.ConstantDescs.INIT_NAME;
import static java.lang.constant.ConstantDescs.MTD_void;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodHandles.Lookup.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class DefineClassTest {
private static final String THIS_PACKAGE = DefineClassTest.class.getPackageName();
private static final ClassDesc CD_Runnable = Runnable.class.describeConstable().orElseThrow();
private static final ClassDesc CD_MissingSuperClass = ClassDesc.of("MissingSuperClass");
/**
* Test that a class has the same class loader, and is in the same package and
* protection domain, as a lookup class.
*/
void testSameAbode(Class<?> clazz, Class<?> lc) {
assertSame(lc.getClassLoader(), clazz.getClassLoader());
assertEquals(lc.getPackageName(), clazz.getPackageName());
assertSame(lc.getProtectionDomain(), clazz.getProtectionDomain());
}
/**
* Tests that a class is discoverable by name using Class.forName and
* lookup.findClass
*/
void testDiscoverable(Class<?> clazz, Lookup lookup) throws Exception {
String cn = clazz.getName();
ClassLoader loader = clazz.getClassLoader();
assertSame(clazz, Class.forName(cn, false, loader));
assertSame(clazz, lookup.findClass(cn));
}
/**
* Basic test of defineClass to define a class in the same package as test.
*/
@Test
public void testDefineClass() throws Exception {
final String CLASS_NAME = THIS_PACKAGE + ".Foo";
Lookup lookup = lookup();
Class<?> clazz = lookup.defineClass(generateClass(CLASS_NAME));
// test name
assertEquals(CLASS_NAME, clazz.getName());
// test loader/package/protection-domain
testSameAbode(clazz, lookup.lookupClass());
// test discoverable
testDiscoverable(clazz, lookup);
// attempt defineClass again
var bytes = generateClass(CLASS_NAME);
assertThrows(LinkageError.class, () -> lookup.defineClass(bytes));
}
/**
* Test public/package/protected/private access from class defined with defineClass.
*/
@Test
public void testAccess() throws Exception {
final String THIS_CLASS = this.getClass().getName();
final String CLASS_NAME = THIS_PACKAGE + ".Runner";
Lookup lookup = lookup();
// public
byte[] classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method1");
testInvoke(lookup.defineClass(classBytes));
// package
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method2");
testInvoke(lookup.defineClass(classBytes));
// protected (same package)
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method3");
testInvoke(lookup.defineClass(classBytes));
// private
classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method4");
Class<?> clazz = lookup.defineClass(classBytes);
Runnable r = (Runnable) clazz.newInstance();
assertThrows(IllegalAccessError.class, r::run);
}
public static void method1() { }
static void method2() { }
protected static void method3() { }
private static void method4() { }
void testInvoke(Class<?> clazz) throws Exception {
Object obj = clazz.newInstance();
((Runnable) obj).run();
}
/**
* Test that defineClass does not run the class initializer
*/
@Test
public void testInitializerNotRun() throws Exception {
final String THIS_CLASS = this.getClass().getName();
final String CLASS_NAME = THIS_PACKAGE + ".ClassWithClinit";
byte[] classBytes = generateClassWithInitializer(CLASS_NAME, THIS_CLASS, "fail");
Class<?> clazz = lookup().defineClass(classBytes);
// trigger initializer to run
var e = assertThrows(ExceptionInInitializerError.class, clazz::newInstance);
assertInstanceOf(IllegalCallerException.class, e.getCause());
}
static void fail() { throw new IllegalCallerException(); }
/**
* Test defineClass to define classes in a package containing classes with
* different protection domains.
*/
@Test
public void testTwoProtectionDomains() throws Exception {
Path here = Paths.get("");
// p.C1 in one exploded directory
Path dir1 = Files.createTempDirectory(here, "classes");
Path p = Files.createDirectory(dir1.resolve("p"));
Files.write(p.resolve("C1.class"), generateClass("p.C1"));
URL url1 = dir1.toUri().toURL();
// p.C2 in another exploded directory
Path dir2 = Files.createTempDirectory(here, "classes");
p = Files.createDirectory(dir2.resolve("p"));
Files.write(p.resolve("C2.class"), generateClass("p.C2"));
URL url2 = dir2.toUri().toURL();
// load p.C1 and p.C2
ClassLoader loader = new URLClassLoader(new URL[] { url1, url2 });
Class<?> target1 = Class.forName("p.C1", false, loader);
Class<?> target2 = Class.forName("p.C2", false, loader);
assertSame(loader, target1.getClassLoader());
assertSame(loader, target1.getClassLoader());
assertNotEquals(target2.getProtectionDomain(), target1.getProtectionDomain());
// protection domain 1
Lookup lookup1 = privateLookupIn(target1, lookup());
Class<?> clazz = lookup1.defineClass(generateClass("p.Foo"));
testSameAbode(clazz, lookup1.lookupClass());
testDiscoverable(clazz, lookup1);
// protection domain 2
Lookup lookup2 = privateLookupIn(target2, lookup());
clazz = lookup2.defineClass(generateClass("p.Bar"));
testSameAbode(clazz, lookup2.lookupClass());
testDiscoverable(clazz, lookup2);
}
/**
* Test defineClass defining a class to the boot loader
*/
@Test
public void testBootLoader() throws Exception {
Lookup lookup = privateLookupIn(Thread.class, lookup());
assertNull(lookup.getClass().getClassLoader());
Class<?> clazz = lookup.defineClass(generateClass("java.lang.Foo"));
assertEquals("java.lang.Foo", clazz.getName());
testSameAbode(clazz, Thread.class);
testDiscoverable(clazz, lookup);
}
@Test
public void testWrongPackage() throws Exception {
assertThrows(IllegalArgumentException.class, () -> lookup().defineClass(generateClass("other.C")));
}
@Test
public void testNoPackageAccess() throws Exception {
Lookup lookup = lookup().dropLookupMode(PACKAGE);
assertThrows(IllegalAccessException.class, () -> lookup.defineClass(generateClass(THIS_PACKAGE + ".C")));
}
@Test
public void testTruncatedClassFile() throws Exception {
assertThrows(ClassFormatError.class, () -> lookup().defineClass(new byte[0]));
}
@Test
public void testNull() throws Exception {
assertThrows(NullPointerException.class, () -> lookup().defineClass(null));
}
@Test
public void testLinking() throws Exception {
assertThrows(NoClassDefFoundError.class, () -> lookup().defineClass(generateNonLinkableClass(THIS_PACKAGE + ".NonLinkableClass")));
}
@Test
public void testModuleInfo() throws Exception {
assertThrows(IllegalArgumentException.class, () -> lookup().defineClass(generateModuleInfo()));
}
/**
* Generates a class file with the given class name
*/
byte[] generateClass(String className) {
return ClassFile.of().build(ClassDesc.of(className), clb -> {
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
clb.withSuperclass(CD_Object);
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
cob.return_();
});
});
}
/**
* Generate a class file with the given class name. The class implements Runnable
* with a run method to invokestatic the given targetClass/targetMethod.
*/
byte[] generateRunner(String className,
String targetClass,
String targetMethod) throws Exception {
return ClassFile.of().build(ClassDesc.of(className), clb -> {
clb.withSuperclass(CD_Object);
clb.withInterfaceSymbols(CD_Runnable);
clb.withMethodBody(INIT_NAME, MTD_void, PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
cob.return_();
});
clb.withMethodBody("run", MTD_void, PUBLIC, cob -> {
cob.invokestatic(ClassDesc.of(targetClass), targetMethod, MTD_void);
cob.return_();
});
});
}
/**
* Generate a class file with the given class name. The class will initializer
* to invokestatic the given targetClass/targetMethod.
*/
byte[] generateClassWithInitializer(String className,
String targetClass,
String targetMethod) throws Exception {
return ClassFile.of().build(ClassDesc.of(className), clb -> {
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
clb.withSuperclass(CD_Object);
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
cob.return_();
});
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
cob.invokestatic(ClassDesc.of(targetClass), targetMethod, MTD_void);
cob.return_();
});
});
}
/**
* Generates a non-linkable class file with the given class name
*/
byte[] generateNonLinkableClass(String className) {
return ClassFile.of().build(ClassDesc.of(className), clb -> {
clb.withFlags(AccessFlag.PUBLIC, AccessFlag.SUPER);
clb.withSuperclass(CD_MissingSuperClass);
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_MissingSuperClass, INIT_NAME, MTD_void);
cob.return_();
});
});
}
/**
* Generates a class file with the given class name
*/
byte[] generateModuleInfo() {
return ClassFile.of().build(ClassDesc.of("module-info"), cb -> cb.withFlags(AccessFlag.MODULE));
}
private int nextNumber() {
return ++nextNumber;
}
private int nextNumber;
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2016, 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 8158169
* @summary unit tests for java.lang.invoke.MethodHandles
* @run junit test.java.lang.invoke.DropArgumentsTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class DropArgumentsTest {
@Test
public void testDropArgumentsToMatch() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodType bigType = cat.type().insertParameterTypes(0, String.class, String.class, int.class);
MethodHandle d0 = MethodHandles.dropArgumentsToMatch(cat, 0, bigType.parameterList(), 3);
assertEquals("xy",(String)d0.invokeExact("m", "n", 1, "x", "y"));
MethodHandle d1 = MethodHandles.dropArgumentsToMatch(cat, 0, bigType.parameterList(), 0);
assertEquals("mn",(String)d1.invokeExact("m", "n", 1, "x", "y"));
MethodHandle d2 = MethodHandles.dropArgumentsToMatch(cat, 1, bigType.parameterList(), 4);
assertEquals("xy",(String)d2.invokeExact("x", "b", "c", 1, "a", "y"));
}
private static Object[][] dropArgumentsToMatchNPEData()
throws NoSuchMethodException, IllegalAccessException {
MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
return new Object[][] {
{ (MethodHandle) null, 0, cat.type().parameterList(), 0 },
{ cat, 0, null, 0 }
};
}
@ParameterizedTest
@MethodSource("dropArgumentsToMatchNPEData")
public void dropArgumentsToMatchNPE(MethodHandle target, int pos, List<Class<?>> valueType, int skip) {
assertThrows(NullPointerException.class, () -> MethodHandles.dropArgumentsToMatch(target, pos, valueType, skip));
}
private static Object[][] dropArgumentsToMatchIAEData()
throws NoSuchMethodException, IllegalAccessException {
MethodHandle cat = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodType bigType = cat.type().insertParameterTypes(0, String.class, String.class, int.class);
return new Object[][] {
{cat, -1, bigType.parameterList(), 0},
{cat, 0, bigType.parameterList(), -1},
{cat, 3, bigType.parameterList(), 0},
{cat, 0, bigType.parameterList(), 6},
{cat, 0, bigType.parameterList(), 2}
};
}
@ParameterizedTest
@MethodSource("dropArgumentsToMatchIAEData")
public void dropArgumentsToMatchIAE(MethodHandle target, int pos, List<Class<?>> valueType, int skip) {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.dropArgumentsToMatch(target, pos, valueType, skip));
}
@Test
public void dropArgumentsToMatchTestWithVoid() throws Throwable {
MethodHandle cat = lookup().findVirtual(String.class, "concat",
MethodType.methodType(String.class, String.class));
List<Class<?>> bigTypewithVoid = new ArrayList<>(cat.type().parameterList());
bigTypewithVoid.addAll(0, List.of(void.class, String.class, int.class));
assertThrows(IllegalArgumentException.class, () ->
MethodHandles.dropArgumentsToMatch(cat, 0, bigTypewithVoid, 1));
}
public static class MethodSet {
static void mVoid() {
}
static void mVoid(int t) {
}
}
@Test
public void dropArgumentsToMatchPosSkipRange() throws Throwable {
// newTypes.size() == 1, pos == 1 && target.paramSize() == 0, skip == 0
MethodHandle mh1 = MethodHandles.lookup().findStatic(MethodSet.class, "mVoid",
MethodType.methodType(void.class));
MethodHandle handle1 = dropArgumentsToMatch(mh1, 0, Collections.singletonList(int.class), 1);
assertEquals(1, handle1.type().parameterList().size());
// newTypes.size() == 1, pos == 0 && target.paramSize() == 1, skip == 1
MethodHandle mh2 = MethodHandles.lookup().findStatic(MethodSet.class, "mVoid",
MethodType.methodType(void.class, int.class));
MethodHandle handle2 = dropArgumentsToMatch(mh2, 1, Collections.singletonList(int.class), 0);
assertEquals(2, handle2.type().parameterList().size());
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2016, 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
* @run junit DropLookupModeTest
* @summary Basic unit tests Lookup::dropLookupMode
*/
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import static java.lang.invoke.MethodHandles.Lookup.*;
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;
import org.junit.jupiter.params.provider.ValueSource;
public class DropLookupModeTest {
/**
* Basic test of dropLookupMode
*/
@Test
public void testBasic() {
final Lookup fullPowerLookup = MethodHandles.lookup();
final Class<?> lc = fullPowerLookup.lookupClass();
assertEquals(PUBLIC | MODULE | PACKAGE | PROTECTED | PRIVATE | ORIGINAL, fullPowerLookup.lookupModes());
Lookup lookup = fullPowerLookup.dropLookupMode(PRIVATE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE | PACKAGE, lookup.lookupModes());
lookup = fullPowerLookup.dropLookupMode(PROTECTED);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE | PACKAGE | PRIVATE, lookup.lookupModes());
lookup = fullPowerLookup.dropLookupMode(PACKAGE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE, lookup.lookupModes());
lookup = fullPowerLookup.dropLookupMode(MODULE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC, lookup.lookupModes());
lookup = fullPowerLookup.dropLookupMode(PUBLIC);
assertSame(lc, lookup.lookupClass());
assertEquals(0, lookup.lookupModes());
lookup = fullPowerLookup.dropLookupMode(UNCONDITIONAL);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE | PACKAGE | PRIVATE, lookup.lookupModes());
}
/**
* Starting with a full power Lookup, use dropLookupMode to create new Lookups
* with reduced access.
*/
@Test
public void testReducingAccess() {
Lookup lookup = MethodHandles.lookup();
final Class<?> lc = lookup.lookupClass();
assertEquals(PUBLIC | MODULE | PACKAGE | PROTECTED | PRIVATE | ORIGINAL, lookup.lookupModes());
lookup = lookup.dropLookupMode(PROTECTED);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE | PACKAGE | PRIVATE, lookup.lookupModes());
lookup = lookup.dropLookupMode(PRIVATE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE | PACKAGE, lookup.lookupModes());
lookup = lookup.dropLookupMode(PACKAGE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC | MODULE, lookup.lookupModes());
lookup = lookup.dropLookupMode(MODULE);
assertSame(lc, lookup.lookupClass());
assertEquals(PUBLIC, lookup.lookupModes());
lookup = lookup.dropLookupMode(PUBLIC);
assertSame(lc, lookup.lookupClass());
assertEquals(0, lookup.lookupModes());
// repeat with lookup has no access
lookup = lookup.dropLookupMode(PUBLIC);
assertSame(lc, lookup.lookupClass());
assertEquals(0, lookup.lookupModes());
}
public static Object[][] unconditionals() {
Lookup publicLookup = MethodHandles.publicLookup();
return new Object[][] {
{ publicLookup, Object.class },
{ publicLookup.in(String.class), String.class },
{ publicLookup.in(DropLookupModeTest.class), DropLookupModeTest.class },
};
}
/**
* Test dropLookupMode on the lookup with public lookup
* and UNCONDITIONAL
*/
@ParameterizedTest
@MethodSource("unconditionals")
public void testUnconditionalLookup(Lookup unconditionalLookup, Class<?> expected) {
assertEquals(UNCONDITIONAL, unconditionalLookup.lookupModes());
assertPublicLookup(unconditionalLookup.dropLookupMode(PRIVATE), expected);
assertPublicLookup(unconditionalLookup.dropLookupMode(PROTECTED), expected);
assertPublicLookup(unconditionalLookup.dropLookupMode(PACKAGE), expected);
assertPublicLookup(unconditionalLookup.dropLookupMode(MODULE), expected);
assertPublicLookup(unconditionalLookup.dropLookupMode(PUBLIC), expected);
// drop all access
Lookup lookup = unconditionalLookup.dropLookupMode(UNCONDITIONAL);
assertSame(expected, lookup.lookupClass());
assertEquals(0, lookup.lookupModes());
}
private void assertPublicLookup(Lookup lookup, Class<?> expected) {
assertSame(expected, lookup.lookupClass());
assertEquals(UNCONDITIONAL, lookup.lookupModes());
}
/**
* Check that IllegalArgumentException is thrown for bad input
*/
@ParameterizedTest
@ValueSource(ints = {
0,
(PACKAGE|PRIVATE), // two modes
Integer.MAX_VALUE,
Integer.MIN_VALUE,
})
public void testBadInput(int modeToDrop) {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.lookup().dropLookupMode(modeToDrop));
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023, 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 8307944
* @library /test/lib
* @build DumpMethodHandleInternals
* @run main DumpMethodHandleInternals
* @summary Test startup with -Djdk.invoke.MethodHandle.dumpMethodHandleInternals
* to work properly
*/
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.test.lib.process.ProcessTools;
public class DumpMethodHandleInternals {
private static final Path DUMP_DIR = Path.of("DUMP_METHOD_HANDLE_INTERNALS");
public static void main(String[] args) throws Exception {
if (ProcessTools.executeTestJava("-Djdk.invoke.MethodHandle.dumpMethodHandleInternals",
"-version")
.outputTo(System.out)
.errorTo(System.out)
.getExitValue() != 0)
throw new RuntimeException("Test failed - see output");
if (Files.notExists(DUMP_DIR))
throw new RuntimeException(DUMP_DIR + " not created");
}
}

View file

@ -0,0 +1,613 @@
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import sun.invoke.util.Wrapper;
import test.java.lang.invoke.lib.Helper;
import java.io.File;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.WrongMethodTypeException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/*
* @test
* @bug 8060483 8066746
* @key randomness
* @library /test/lib /java/lang/invoke/common
* @modules java.base/sun.invoke.util
* @summary unit tests for MethodHandles.explicitCastArguments()
* @run main ExplicitCastArgumentsTest
*/
/**
* Tests for MethodHandles.explicitCastArguments().
*/
public class ExplicitCastArgumentsTest {
private static final boolean VERBOSE = Helper.IS_VERBOSE;
private static final Class<?> THIS_CLASS = ExplicitCastArgumentsTest.class;
private static final Random RNG = Helper.RNG;
private static final Map<Wrapper, Object> RANDOM_VALUES = new HashMap<>(9);
static {
RANDOM_VALUES.put(Wrapper.BOOLEAN, RNG.nextBoolean());
RANDOM_VALUES.put(Wrapper.BYTE, (byte) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.SHORT, (short) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.CHAR, (char) RNG.nextInt());
RANDOM_VALUES.put(Wrapper.INT, RNG.nextInt());
RANDOM_VALUES.put(Wrapper.LONG, RNG.nextLong());
RANDOM_VALUES.put(Wrapper.FLOAT, RNG.nextFloat());
RANDOM_VALUES.put(Wrapper.DOUBLE, RNG.nextDouble());
RANDOM_VALUES.put(Wrapper.OBJECT, new Object());
}
public static void main(String[] args) throws Throwable {
testVarargsCollector();
testNullRef2Prim();
testRef2Prim();
testPrim2Ref();
testPrim2Prim();
testNonBCPRef2NonBCPRef();
testBCPRef2BCPRef();
testNonBCPRef2BCPRef();
testReturnAny2Void();
testReturnVoid2Any();
testMultipleArgs();
System.out.println("TEST PASSED");
}
/**
* Dummy method used in {@link #testVarargsCollector} test to form a method
* handle.
*
* @param args - any args
* @return - returns args
*/
public static String[] f(String... args) {
return args;
}
/**
* Tests that MHs.explicitCastArguments does incorrect type checks for
* VarargsCollector. Bug 8066746.
*
* @throws java.lang.Throwable
*/
public static void testVarargsCollector() throws Throwable {
MethodType mt = MethodType.methodType(String[].class, String[].class);
MethodHandle mh = MethodHandles.publicLookup()
.findStatic(THIS_CLASS, "f", mt);
mh = MethodHandles.explicitCastArguments(mh,
MethodType.methodType(Object.class, Object.class));
mh.invokeWithArguments((Object) (new String[]{"str1", "str2"}));
}
/**
* Tests that null wrapper reference is successfully converted to primitive
* types. Converted result should be zero for a primitive. Bug 8060483.
*/
public static void testNullRef2Prim() {
for (Wrapper from : Wrapper.values()) {
for (Wrapper to : Wrapper.values()) {
if (from == Wrapper.VOID || to == Wrapper.VOID) {
continue;
}
// MHs.eCA javadoc:
// If T0 is a reference and T1 a primitive, and if the reference
// is null at runtime, a zero value is introduced.
for (TestConversionMode mode : TestConversionMode.values()) {
testConversion(mode, from.wrapperType(),
to.primitiveType(), null, to.zero(), false, null);
}
}
}
}
/**
* Tests that non-null wrapper reference is successfully converted to
* primitive types.
*/
public static void testRef2Prim() {
for (Wrapper from : Wrapper.values()) {
for (Wrapper to : Wrapper.values()) {
if (from == Wrapper.VOID || to == Wrapper.VOID
|| to == Wrapper.OBJECT) {
continue;
}
Object value = RANDOM_VALUES.get(from);
for (TestConversionMode mode : TestConversionMode.values()) {
if (from != Wrapper.OBJECT) {
Object convValue = to.wrap(value);
testConversion(mode, from.wrapperType(),
to.primitiveType(), value, convValue, false, null);
} else {
testConversion(mode, from.wrapperType(),
to.primitiveType(), value, null,
true, ClassCastException.class);
}
}
}
}
}
/**
* Tests that primitive is successfully converted to wrapper reference
* types, to the Number type (if possible) and to the Object type.
*/
public static void testPrim2Ref() {
for (Wrapper from : Wrapper.values()) {
for (Wrapper to : Wrapper.values()) {
if (from == Wrapper.VOID || from == Wrapper.OBJECT
|| to == Wrapper.VOID || to == Wrapper.OBJECT) {
continue;
}
Object value = RANDOM_VALUES.get(from);
for (TestConversionMode mode : TestConversionMode.values()) {
if (from == to) {
testConversion(mode, from.primitiveType(),
to.wrapperType(), value, value, false, null);
} else {
testConversion(mode, from.primitiveType(),
to.wrapperType(), value, null, true, ClassCastException.class);
}
if (from != Wrapper.BOOLEAN && from != Wrapper.CHAR) {
testConversion(mode, from.primitiveType(),
Number.class, value, value, false, null);
} else {
testConversion(mode, from.primitiveType(),
Number.class, value, null,
true, ClassCastException.class);
}
testConversion(mode, from.primitiveType(),
Object.class, value, value, false, null);
}
}
}
}
/**
* Tests that primitive is successfully converted to other primitive type.
*/
public static void testPrim2Prim() {
for (Wrapper from : Wrapper.values()) {
for (Wrapper to : Wrapper.values()) {
if (from == Wrapper.VOID || to == Wrapper.VOID
|| from == Wrapper.OBJECT || to == Wrapper.OBJECT) {
continue;
}
Object value = RANDOM_VALUES.get(from);
Object convValue = to.wrap(value);
for (TestConversionMode mode : TestConversionMode.values()) {
testConversion(mode, from.primitiveType(),
to.primitiveType(), value, convValue, false, null);
}
}
}
}
/**
* Dummy interface for {@link #testNonBCPRef2Ref} test.
*/
public static interface TestInterface {}
/**
* Dummy class for {@link #testNonBCPRef2Ref} test.
*/
public static class TestSuperClass implements TestInterface {}
/**
* Dummy class for {@link #testNonBCPRef2Ref} test.
*/
public static class TestSubClass1 extends TestSuperClass {}
/**
* Dummy class for {@link #testNonBCPRef2Ref} test.
*/
public static class TestSubClass2 extends TestSuperClass {}
/**
* Tests non-bootclasspath reference to reference conversions.
*
* @throws java.lang.Throwable
*/
public static void testNonBCPRef2NonBCPRef() throws Throwable {
Class testInterface = TestInterface.class;
Class testSuperClass = TestSuperClass.class;
Class testSubClass1 = TestSubClass1.class;
Class testSubClass2 = TestSubClass2.class;
Object testSuperObj = new TestSuperClass();
Object testObj01 = new TestSubClass1();
Object testObj02 = new TestSubClass2();
Class[] parents = {testInterface, testSuperClass};
Class[] children = {testSubClass1, testSubClass2};
Object[] childInst = {testObj01, testObj02};
for (TestConversionMode mode : TestConversionMode.values()) {
for (Class parent : parents) {
for (int j = 0; j < children.length; j++) {
// Child type to parent type non-null conversion, shoud succeed
testConversion(mode, children[j], parent, childInst[j],
childInst[j], false, null);
// Child type to parent type null conversion, shoud succeed
testConversion(mode, children[j], parent, null,
null, false, null);
// Parent type to child type non-null conversion with parent
// type instance, should fail
testConversion(mode, parent, children[j], testSuperObj,
null, true, ClassCastException.class);
// Parent type to child type non-null conversion with child
// type instance, should succeed
testConversion(mode, parent, children[j], childInst[j],
childInst[j], false, null);
// Parent type to child type null conversion, should succeed
testConversion(mode, parent, children[j], null,
null, false, null);
}
// Parent type to child type non-null conversion with sibling
// type instance, should fail
testConversion(mode, parent, testSubClass1, testObj02,
null, true, ClassCastException.class);
}
// Sibling type non-null conversion, should fail
testConversion(mode, testSubClass1,
testSubClass2, testObj01, null, true,
ClassCastException.class);
// Sibling type null conversion, should succeed
testConversion(mode, testSubClass1,
testSubClass2, null, null, false, null);
}
}
/**
* Dummy interface for {@link #testNonBCPRef2BCPRef} test.
*/
public static interface TestSerializableInterface extends Serializable {}
/**
* Dummy class for {@link #testNonBCPRef2BCPRef} test.
*/
public static class TestSerializableClass
implements TestSerializableInterface {}
/**
* Dummy class for {@link #testNonBCPRef2BCPRef} test.
*/
public static class TestFileChildClass extends File
implements TestSerializableInterface {
public TestFileChildClass(String pathname) {
super(pathname);
}
}
/**
* Tests non-bootclasspath reference to bootclasspath reference conversions
* and vice-versa.
*
* @throws java.lang.Throwable
*/
public static void testNonBCPRef2BCPRef() throws Throwable {
Class bcpInterface = Serializable.class;
Class bcpSuperClass = File.class;
Class nonBcpInterface = TestSerializableInterface.class;
Class nonBcpSuperSiblingClass = TestSerializableClass.class;
Class nonBcpSubClass = TestFileChildClass.class;
Object bcpSuperObj = new File(".");
Object testSuperSiblingObj = new TestSerializableClass();
Object testSubObj = new TestFileChildClass(".");
Class[] parents = {bcpInterface, bcpSuperClass};
for (TestConversionMode mode : TestConversionMode.values()) {
for (Class parent : parents) {
// Child type to parent type non-null conversion, shoud succeed
testConversion(mode, nonBcpSubClass, parent, testSubObj,
testSubObj, false, null);
// Child type to parent type null conversion, shoud succeed
testConversion(mode, nonBcpSubClass, parent, null, null,
false, null);
// Parent type to child type non-null conversion with parent
// type instance, should fail
testConversion(mode, parent, nonBcpSubClass, bcpSuperObj, null,
true, ClassCastException.class);
// Parent type to child type non-null conversion with child
// type instance, should succeed
testConversion(mode, parent, nonBcpSubClass, testSubObj,
testSubObj, false, null);
// Parent type to child type null conversion, should succeed
testConversion(mode, parent, nonBcpSubClass, null, null,
false, null);
}
// Parent type to child type non-null conversion with
// super sibling type instance, should fail
testConversion(mode, bcpInterface, nonBcpSubClass,
testSuperSiblingObj, null, true, ClassCastException.class);
Class[] siblings = {nonBcpSubClass, bcpSuperClass};
for (Class sibling : siblings) {
// Non-bcp class to bcp/non-bcp sibling class non-null
// conversion with nonBcpSuperSiblingClass instance, should fail
testConversion(mode, nonBcpSuperSiblingClass, sibling,
testSuperSiblingObj, null, true, ClassCastException.class);
// Non-bcp class to bcp/non-bcp sibling class null conversion,
// should succeed
testConversion(mode, nonBcpSuperSiblingClass, sibling,
null, null, false, null);
// Non-bcp interface to bcp/non-bcp sibling class non-null
// conversion with nonBcpSubClass instance, should succeed
testConversion(mode, nonBcpInterface, sibling, testSubObj,
testSubObj, false, null);
// Non-bcp interface to bcp/non-bcp sibling class
// null conversion, should succeed
testConversion(mode, nonBcpInterface, sibling, null, null,
false, null);
// Non-bcp interface to bcp/non-bcp sibling class non-null
// conversion with nonBcpSuperSiblingClass instance, should fail
testConversion(mode, nonBcpInterface, sibling,
testSuperSiblingObj, testSubObj,
true, ClassCastException.class);
}
}
}
/**
* Tests bootclasspath reference to reference conversions.
*/
public static void testBCPRef2BCPRef() {
Class bcpInterface = CharSequence.class;
Class bcpSubClass1 = String.class;
Class bcpSubClass2 = StringBuffer.class;
Object testObj01 = new String("test");
Object testObj02 = new StringBuffer("test");
Class[] children = {bcpSubClass1, bcpSubClass2};
Object[] childInst = {testObj01, testObj02};
for (TestConversionMode mode : TestConversionMode.values()) {
for (int i = 0; i < children.length; i++) {
// Child type to parent type non-null conversion, shoud succeed
testConversion(mode, children[i], bcpInterface, childInst[i],
childInst[i], false, null);
// Child type to parent type null conversion, shoud succeed
testConversion(mode, children[i], bcpInterface, null,
null, false, null);
// Parent type to child type non-null conversion with child
// type instance, should succeed
testConversion(mode, bcpInterface,
children[i], childInst[i], childInst[i], false, null);
// Parent type to child type null conversion, should succeed
testConversion(mode, bcpInterface,
children[i], null, null, false, null);
}
// Sibling type non-null conversion, should fail
testConversion(mode, bcpSubClass1,
bcpSubClass2, testObj01, null, true,
ClassCastException.class);
// Sibling type null conversion, should succeed
testConversion(mode, bcpSubClass1,
bcpSubClass2, null, null, false, null);
// Parent type to child type non-null conversion with sibling
// type instance, should fail
testConversion(mode, bcpInterface, bcpSubClass1, testObj02,
null, true, ClassCastException.class);
}
}
/**
* Dummy method used in {@link #testReturnAny2Void} and
* {@link #testReturnVoid2Any} tests to form a method handle.
*/
public static void retVoid() {}
/**
* Tests that non-null any return is successfully converted to non-type
* void.
*/
public static void testReturnAny2Void() {
for (Wrapper from : Wrapper.values()) {
testConversion(TestConversionMode.RETURN_VALUE, from.wrapperType(),
void.class, RANDOM_VALUES.get(from),
null, false, null);
testConversion(TestConversionMode.RETURN_VALUE, from.primitiveType(),
void.class, RANDOM_VALUES.get(from),
null, false, null);
}
}
/**
* Tests that void return is successfully converted to primitive and
* reference. Result should be zero for primitives and null for references.
*/
public static void testReturnVoid2Any() {
for (Wrapper to : Wrapper.values()) {
testConversion(TestConversionMode.RETURN_VALUE, void.class,
to.primitiveType(), null,
to.zero(), false, null);
testConversion(TestConversionMode.RETURN_VALUE, void.class,
to.wrapperType(), null,
null, false, null);
}
}
private static void checkForWrongMethodTypeException(MethodHandle mh, MethodType mt) {
try {
MethodHandles.explicitCastArguments(mh, mt);
throw new AssertionError("Expected WrongMethodTypeException is not thrown");
} catch (WrongMethodTypeException wmte) {
if (VERBOSE) {
System.out.printf("Expected exception %s: %s\n",
wmte.getClass(), wmte.getMessage());
}
}
}
/**
* Tests that MHs.eCA method works correctly with MHs with multiple arguments.
* @throws Throwable
*/
public static void testMultipleArgs() throws Throwable {
int arity = 1 + RNG.nextInt(Helper.MAX_ARITY / 2 - 2);
int arityMinus = RNG.nextInt(arity);
int arityPlus = arity + RNG.nextInt(Helper.MAX_ARITY / 2 - arity) + 1;
MethodType mType = Helper.randomMethodTypeGenerator(arity);
MethodType mTypeNew = Helper.randomMethodTypeGenerator(arity);
MethodType mTypeNewMinus = Helper.randomMethodTypeGenerator(arityMinus);
MethodType mTypeNewPlus = Helper.randomMethodTypeGenerator(arityPlus);
Class<?> rType = mType.returnType();
MethodHandle original;
if (rType.equals(void.class)) {
MethodType mt = MethodType.methodType(void.class);
original = MethodHandles.publicLookup()
.findStatic(THIS_CLASS, "retVoid", mt);
} else {
Object rValue = Helper.castToWrapper(1, rType);
original = MethodHandles.constant(rType, rValue);
}
original = Helper.addTrailingArgs(original, arity, mType.parameterList());
MethodHandle target = MethodHandles
.explicitCastArguments(original, mTypeNew);
Object[] parList = Helper.randomArgs(mTypeNew.parameterList());
for (int i = 0; i < parList.length; i++) {
if (parList[i] instanceof String) {
parList[i] = null; //getting rid of Strings produced by randomArgs
}
}
target.invokeWithArguments(parList);
checkForWrongMethodTypeException(original, mTypeNewMinus);
checkForWrongMethodTypeException(original, mTypeNewPlus);
}
/**
* Enumeration of test conversion modes.
*/
public enum TestConversionMode {
RETURN_VALUE,
ARGUMENT;
}
/**
* Tests type and value conversion. Comparing with the given expected result.
*
* @param mode - test conversion mode. See {@link #TestConversionMode}.
* @param from - source type.
* @param to - destination type.
* @param param - value to be converted.
* @param expectedResult - expected value after conversion.
* @param failureExpected - true if conversion failure expected.
* @param expectedException - expected exception class if
* {@code failureExpected} is true.
*/
public static void testConversion(TestConversionMode mode,
Class<?> from, Class<?> to, Object param,
Object expectedResult, boolean failureExpected,
Class<? extends Throwable> expectedException) {
if (VERBOSE) {
System.out.printf("Testing return value conversion: "
+ "%-10s => %-10s: %5s: ", from.getSimpleName(),
to.getSimpleName(), param);
}
MethodHandle original = null;
MethodType newType = null;
switch (mode) {
case RETURN_VALUE:
if (from.equals(void.class)) {
MethodType mt = MethodType.methodType(void.class);
try {
original = MethodHandles.publicLookup()
.findStatic(THIS_CLASS, "retVoid", mt);
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new Error("Unexpected issue", ex);
}
} else {
original = MethodHandles.constant(from, param);
}
newType = original.type().changeReturnType(to);
break;
case ARGUMENT:
if (from.equals(void.class) || to.equals(void.class)) {
throw new Error("Test issue: argument conversion does not"
+ " work with non-type void");
}
original = MethodHandles.identity(to);
newType = original.type().changeParameterType(0, from);
break;
default:
String msg = String.format("Test issue: unknown test"
+ " convertion mode %s.", mode.name());
throw new Error(msg);
}
try {
MethodHandle target = MethodHandles
.explicitCastArguments(original, newType);
Object result;
switch (mode) {
case RETURN_VALUE:
result = target.invokeWithArguments();
break;
case ARGUMENT:
result = target.invokeWithArguments(param);
break;
default:
String msg = String.format("Test issue: unknown test"
+ " convertion mode %s.", mode.name());
throw new Error(msg);
}
if (!failureExpected
&& (expectedResult != null && !expectedResult.equals(result)
|| expectedResult == null && result != null)) {
String msg = String.format("Conversion result %s is not equal"
+ " to the expected result %10s",
result, expectedResult);
throw new AssertionError(msg);
}
if (VERBOSE) {
String resultStr;
if (result != null) {
resultStr = String.format("Converted value and type are"
+ " %10s (%10s)", "'" + result + "'",
result.getClass().getSimpleName());
} else {
resultStr = String.format("Converted value is %10s", result);
}
System.out.println(resultStr);
}
if (failureExpected) {
String msg = String.format("No exception thrown while testing"
+ " return value conversion: %10s => %10s;"
+ " parameter: %10s",
from, to, param);
throw new AssertionError(msg);
}
} catch (AssertionError e) {
throw e; // report test failure
} catch (Throwable e) {
if (VERBOSE) {
System.out.printf("%s: %s\n", e.getClass(), e.getMessage());
}
if (!failureExpected || !e.getClass().equals(expectedException)) {
String msg = String.format("Unexpected exception was thrown"
+ " while testing return value conversion:"
+ " %s => %s; parameter: %s", from, to, param);
throw new AssertionError(msg, e);
}
}
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018, 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 8194554
* @run junit/othervm test.java.lang.invoke.FilterArgumentsTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class FilterArgumentsTest {
@Test
public void testFilterA_B_C() throws Throwable {
FilterTest test = new FilterTest(
filterArguments(MH_TEST, 0, MH_FILTER_A, MH_FILTER_B, MH_FILTER_C));
test.run(List.of("A", "B", "C"));
}
@Test
public void testFilterA_B() throws Throwable {
FilterTest test = new FilterTest(
filterArguments(MH_TEST, 0, MH_FILTER_A, MH_FILTER_B));
test.run(List.of("A", "B"));
}
@Test
public void testFilterB_C() throws Throwable {
FilterTest test = new FilterTest(
filterArguments(MH_TEST, 1, MH_FILTER_B, MH_FILTER_C));
test.run(List.of("B", "C"));
}
@Test
public void testFilterB() throws Throwable {
FilterTest test = new FilterTest(filterArguments(MH_TEST, 1, MH_FILTER_B));
test.run(List.of("B"));
}
@Test
public void testFilterC() throws Throwable {
FilterTest test = new FilterTest(filterArguments(MH_TEST, 2, MH_FILTER_C));
test.run(List.of("C"));
}
static class FilterTest {
static List<String> filters = new ArrayList<>();
final MethodHandle mh;
FilterTest(MethodHandle mh) {
this.mh = mh;
}
void run(List<String> expected) throws Throwable {
filters.clear();
assertEquals("x-0-z", (String)mh.invokeExact("x", 0, 'z'));
assertEquals(expected, filters);
}
static String filterA(String s) {
filters.add("A");
return s;
}
static int filterB(int value) {
filters.add("B");
return value;
}
static char filterC(char c) {
filters.add("C");
return c;
}
static String test(String s, int i, char c) {
return s + "-" + i + "-" + c;
}
}
static final MethodHandle MH_TEST;
static final MethodHandle MH_FILTER_A;
static final MethodHandle MH_FILTER_B;
static final MethodHandle MH_FILTER_C;
static final Lookup LOOKUP = MethodHandles.lookup();
static {
try {
MH_TEST = LOOKUP.findStatic(FilterTest.class, "test",
methodType(String.class, String.class, int.class, char.class));
MH_FILTER_A = LOOKUP.findStatic(FilterTest.class, "filterA",
methodType(String.class, String.class));
MH_FILTER_B = LOOKUP.findStatic(FilterTest.class, "filterB",
methodType(int.class, int.class));
MH_FILTER_C = LOOKUP.findStatic(FilterTest.class, "filterC",
methodType(char.class, char.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8200167 8010319
* @summary Regression test for a bug introduced in 8200167 and fixed in 8010319
* @run main FinalVirtualCallFromInterface
*/
import java.lang.invoke.*;
/*
* Test that a MethodHandle for a final method, called from an interface, works correctly.
* With only 8200167 applied this fails with:
* Exception in thread "main" java.lang.InternalError: Should only be invoked on a subclass
* at
* java.base/java.lang.invoke.DirectMethodHandle.checkReceiver(DirectMethodHandle.java:441)
*
* The nestmate update under 8010319 fixes that bug.
*/
public class FinalVirtualCallFromInterface {
static class Final {
public final void fm() {}
}
static interface FinalUser {
static void test() throws Throwable {
MethodType mt = MethodType.methodType(void.class);
MethodHandle mh = MethodHandles.lookup().findVirtual(Final.class, "fm", mt);
Final f = new Final();
mh.invokeExact(f);
mh.invoke(f);
}
}
public static void main(String[] args) throws Throwable {
FinalUser.test();
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2015, 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 8139885
* @run junit/othervm -ea -esa test.java.lang.invoke.FindAccessTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Tests for Lookup.findClass/accessClass extensions added in JEP 274.
*/
public class FindAccessTest {
static final Lookup LOOKUP = MethodHandles.lookup();
@Test
public void testFindSpecial() throws Throwable {
FindSpecial.C c = new FindSpecial.C();
assertEquals("I1.m", c.m());
MethodType t = MethodType.methodType(String.class);
MethodHandle ci1m = LOOKUP.findSpecial(FindSpecial.I1.class, "m", t, FindSpecial.C.class);
assertEquals("I1.m", (String) ci1m.invoke(c));
}
@Test
public void testFindSpecialAbstract() throws Throwable {
FindSpecial.C c = new FindSpecial.C();
assertEquals("q", c.q());
MethodType t = MethodType.methodType(String.class);
var thrown = assertThrows(IllegalAccessException.class,
() -> LOOKUP.findSpecial(FindSpecial.I3.class, "q", t, FindSpecial.C.class));
assertEquals(FindSpecial.ABSTRACT_ERROR, thrown.getMessage());
}
@Test
public void testFindClassCNFE() throws ClassNotFoundException, IllegalAccessException {
assertThrows(ClassNotFoundException.class, () -> LOOKUP.findClass("does.not.Exist"));
}
static class FindSpecial {
interface I1 {
default String m() {
return "I1.m";
}
}
interface I2 {
default String m() {
return "I2.m";
}
}
interface I3 {
String q();
}
static class C implements I1, I2, I3 {
public String m() {
return I1.super.m();
}
public String q() {
return "q";
}
}
static final String ABSTRACT_ERROR = "no such method: test.java.lang.invoke.FindAccessTest$FindSpecial$I3.q()String/invokeSpecial";
}
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2015, 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 8139885
* @run junit/othervm -ea -esa test.java.lang.invoke.FoldTest
*/
package test.java.lang.invoke;
import java.io.StringWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import static java.lang.invoke.MethodType.methodType;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Tests for the new fold method handle combinator added in JEP 274.
*/
public class FoldTest {
static final Lookup LOOKUP = MethodHandles.lookup();
@Test
public void testFold0a() throws Throwable {
// equivalence to foldArguments(MethodHandle,MethodHandle)
MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder);
assertEquals(Fold.MT_folded1, fold.type());
assertEquals(720, (int) fold.invoke(3, 4, 5));
}
@Test
public void testFold1a() throws Throwable {
// test foldArguments for folding position 1
MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1);
assertEquals(Fold.MT_folded1, fold.type());
assertEquals(540, (int) fold.invoke(3, 4, 5));
}
@Test
public void testFold0b() throws Throwable {
// test foldArguments equivalence with multiple types
MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb);
assertEquals(Fold.MT_folded2, fold.type());
assertEquals(23, (int) fold.invoke("true", true, 23));
}
@Test
public void testFold1b() throws Throwable {
// test folgArguments for folding position 1, with multiple types
MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2);
assertEquals(Fold.MT_folded3, fold.type());
assertEquals(1, (int) fold.invoke(true, true, 1));
assertEquals(-1, (int) fold.invoke(true, false, -1));
}
@Test
public void testFoldArgumentsExample() throws Throwable {
// test the JavaDoc foldArguments-with-pos example
StringWriter swr = new StringWriter();
MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr);
MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class));
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace);
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
assertEquals("jum", swr.toString());
}
static class Fold {
static int adder(int a, int b, int c) {
return a + b + c;
}
static int adder1(int a, int b) {
return a + b;
}
static int multer(int x, int q, int r, int s) {
return x * q * r * s;
}
static int str(boolean b1, String s, boolean b2, int x) {
return b1 && s.equals(String.valueOf(b2)) ? x : -x;
}
static boolean comb(String s, boolean b2) {
return !s.equals(b2);
}
static String comb2(boolean b2, int x) {
int ib = b2 ? 1 : 0;
return ib == x ? "true" : "false";
}
static final Class<Fold> FOLD = Fold.class;
static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class);
static final MethodType MT_adder1 = methodType(int.class, int.class, int.class);
static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class);
static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class);
static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class);
static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class);
static final MethodHandle MH_adder;
static final MethodHandle MH_adder1;
static final MethodHandle MH_multer;
static final MethodHandle MH_str;
static final MethodHandle MH_comb;
static final MethodHandle MH_comb2;
static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class);
static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class);
static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class);
static {
try {
MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder);
MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1);
MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer);
MH_str = LOOKUP.findStatic(FOLD, "str", MT_str);
MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb);
MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
}
}

View file

@ -0,0 +1,224 @@
/*
* Copyright (c) 2010, 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.
*/
/* @test
* @bug 7050328 8007035
* @summary smoke test for invokedynamic instructions
* @build indify.Indify
* @compile InvokeDynamicPrintArgs.java
* @run main/othervm
* indify.Indify
* --verify-specifier-count=8
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.InvokeDynamicPrintArgs
*/
package test.java.lang.invoke;
import java.util.*;
import java.io.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
public class InvokeDynamicPrintArgs {
public static void main(String... av) throws Throwable {
openBuf();
System.out.println("Printing some argument lists, starting with a empty one:");
INDY_nothing().invokeExact(); // BSM specifier #0 = {bsm}
INDY_bar().invokeExact("bar arg", 1); // BSM specifier #1 = {bsm2, Void.class, "void type"}
INDY_bar2().invokeExact("bar2 arg", 222); // BSM specifier #1 = (same)
INDY_baz().invokeExact("baz arg", 2, 3.14); // BSM specifier #2 = {bsm2, 1234.5}
INDY_foo().invokeExact("foo arg"); // BSM specifier #0 = (same)
// Hence, BSM specifier count should be 3. See "--verify-specifier-count=3" above.
System.out.println("Done printing argument lists.");
closeBuf();
checkConstantRefs();
}
private static void checkConstantRefs() throws Throwable {
// check some constant references to its self class
assertEquals(MT_bsm(), MH_bsm().type());
assertEquals(MT_bsm2(), MH_bsm2().type());
assertEquals(MT_bsm(), non_MH_bsm().type());
}
private static void assertEquals(Object exp, Object act) {
if (exp == act || (exp != null && exp.equals(act))) return;
throw new AssertionError("not equal: "+exp+", "+act);
}
private static PrintStream oldOut;
private static ByteArrayOutputStream buf;
private static void openBuf() {
oldOut = System.out;
buf = new ByteArrayOutputStream();
System.setOut(new PrintStream(buf));
}
private static void closeBuf() {
if (buf == null) return;
System.out.flush();
System.setOut(oldOut);
String[] haveLines = new String(buf.toByteArray()).split("[\n\r]+");
for (String line : haveLines) System.out.println(line);
Iterator<String> iter = Arrays.asList(haveLines).iterator();
for (String want : EXPECT_OUTPUT) {
String have = iter.hasNext() ? iter.next() : "[EOF]";
if (want.equals(have)) continue;
System.err.println("want line: "+want);
System.err.println("have line: "+have);
throw new AssertionError("unexpected output: "+have);
}
if (iter.hasNext())
throw new AssertionError("unexpected output: "+iter.next());
}
private static final String[] EXPECT_OUTPUT = {
"Printing some argument lists, starting with a empty one:",
"[test.java.lang.invoke.InvokeDynamicPrintArgs, nothing, ()void][]",
"[test.java.lang.invoke.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
"[test.java.lang.invoke.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
"[test.java.lang.invoke.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
"[test.java.lang.invoke.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
"Done printing argument lists."
};
private static boolean doPrint = true;
private static void printArgs(Object bsmInfo, Object... args) {
String message = bsmInfo+Arrays.deepToString(args);
if (doPrint) System.out.println(message);
}
private static MethodHandle MH_printArgs() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(),
"printArgs", methodType(void.class, Object.class, Object[].class));
}
private static CallSite bsm(Lookup caller, String name, MethodType type) throws ReflectiveOperationException {
// ignore caller and name, but match the type:
Object bsmInfo = Arrays.asList(caller, name, type);
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
}
private static MethodType MT_bsm() {
shouldNotCallThis();
return methodType(CallSite.class, Lookup.class, String.class, MethodType.class);
}
private static MethodHandle MH_bsm() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
}
private static MethodHandle non_MH_bsm() throws ReflectiveOperationException {
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
}
/* Example of a constant call site with user-data.
* In this case, the user data is exactly the BSM data.
* Note that a CCS with user data must use the "hooked" constructor
* to bind the CCS itself into the resulting target.
* A normal constructor would not allow a circular relation
* between the CCS and its target.
*/
public static class PrintingCallSite extends ConstantCallSite {
final Lookup caller;
final String name;
final Object[] staticArgs;
PrintingCallSite(Lookup caller, String name, MethodType type, Object... staticArgs) throws Throwable {
super(type, MH_createTarget());
this.caller = caller;
this.name = name;
this.staticArgs = staticArgs;
}
public MethodHandle createTarget() {
try {
return lookup().bind(this, "runTarget", genericMethodType(0, true)).asType(type());
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
public Object runTarget(Object... dynamicArgs) {
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type()));
bsmInfo.addAll(Arrays.asList(staticArgs));
printArgs(bsmInfo, dynamicArgs);
return null;
}
private static MethodHandle MH_createTarget() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findVirtual(lookup().lookupClass(), "createTarget", methodType(MethodHandle.class));
}
}
private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws Throwable {
// ignore caller and name, but match the type:
return new PrintingCallSite(caller, name, type, arg);
}
private static MethodType MT_bsm2() {
shouldNotCallThis();
return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class);
}
private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(lookup().lookupClass(), "bsm2", MT_bsm2());
}
private static MethodHandle INDY_nothing() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(),
"nothing", methodType(void.class)
)).dynamicInvoker();
}
private static MethodHandle INDY_foo() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm().invoke(lookup(),
"foo", methodType(void.class, String.class)
)).dynamicInvoker();
}
private static MethodHandle INDY_bar() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm2().invoke(lookup(),
"bar", methodType(void.class, String.class, int.class)
, Void.class, "void type!", 1, 234.5F, 67.5, (long)89
)).dynamicInvoker();
}
private static MethodHandle INDY_bar2() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm2().invoke(lookup(),
"bar2", methodType(void.class, String.class, int.class)
, Void.class, "void type!", 1, 234.5F, 67.5, (long)89
)).dynamicInvoker();
}
private static MethodHandle INDY_baz() throws Throwable {
shouldNotCallThis();
return ((CallSite) MH_bsm2().invoke(lookup(),
"baz", methodType(void.class, String.class, int.class, double.class)
, 1234.5
)).dynamicInvoker();
}
private static void shouldNotCallThis() {
// if this gets called, the transformation has not taken place
if (System.getProperty("InvokeDynamicPrintArgs.allow-untransformed") != null) return;
throw new AssertionError("this code should be statically transformed away by Indify");
}
}

View file

@ -0,0 +1,520 @@
/*
* Copyright (c) 2009, 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
* @summary unit tests for java.lang.invoke.MethodHandle.invoke
* @compile InvokeGenericTest.java
* @run junit/othervm test.java.lang.invoke.InvokeGenericTest
*/
package test.java.lang.invoke;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import java.lang.reflect.*;
import java.util.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
/**
*
* @author jrose
*/
@SuppressWarnings("cast") // various casts help emphasize arguments to invokeExact
public class InvokeGenericTest {
// How much output?
static int verbosity = 0;
static {
String vstr = System.getProperty("test.java.lang.invoke.InvokeGenericTest.verbosity");
if (vstr != null) verbosity = Integer.parseInt(vstr);
}
// public static void main(String... av) throws Throwable {
// new InvokeGenericTest().testFirst();
// }
@Test
public void testFirst() throws Throwable {
verbosity += 9; try {
// left blank for debugging
} finally { printCounts(); verbosity -= 9; }
}
public InvokeGenericTest() {
}
String testName;
static int allPosTests, allNegTests;
int posTests, negTests;
@AfterEach
public void printCounts() {
if (verbosity >= 2 && (posTests | negTests) != 0) {
System.out.println();
if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run");
if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run");
allPosTests += posTests;
allNegTests += negTests;
posTests = negTests = 0;
}
}
void countTest(boolean positive) {
if (positive) ++posTests;
else ++negTests;
}
void countTest() { countTest(true); }
void startTest(String name) {
if (testName != null) printCounts();
if (verbosity >= 1)
System.out.println("["+name+"]");
posTests = negTests = 0;
testName = name;
}
@BeforeAll
public static void setUpClass() throws Exception {
calledLog.clear();
calledLog.add(null);
nextArgVal = INITIAL_ARG_VAL;
}
@AfterAll
public static void tearDownClass() throws Exception {
int posTests = allPosTests, negTests = allNegTests;
if (verbosity >= 2 && (posTests | negTests) != 0) {
System.out.println();
if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases");
if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases");
}
}
static List<Object> calledLog = new ArrayList<>();
static Object logEntry(String name, Object... args) {
return Arrays.asList(name, Arrays.asList(args));
}
static Object called(String name, Object... args) {
Object entry = logEntry(name, args);
calledLog.add(entry);
return entry;
}
static void assertCalled(String name, Object... args) {
Object expected = logEntry(name, args);
Object actual = calledLog.get(calledLog.size() - 1);
if (expected.equals(actual) && verbosity < 9) return;
System.out.println("assertCalled "+name+":");
System.out.println("expected: "+expected);
System.out.println("actual: "+actual);
System.out.println("ex. types: "+getClasses(expected));
System.out.println("act. types: "+getClasses(actual));
assertEquals(expected, actual, "previous method call");
}
static void printCalled(MethodHandle target, String name, Object... args) {
if (verbosity >= 3)
System.out.println("calling MH="+target+" to "+name+Arrays.toString(args));
}
static Object castToWrapper(Object value, Class<?> dst) {
Object wrap = null;
if (value instanceof Number)
wrap = castToWrapperOrNull(((Number)value).longValue(), dst);
if (value instanceof Character)
wrap = castToWrapperOrNull((char)(Character)value, dst);
if (wrap != null) return wrap;
return dst.cast(value);
}
static Object castToWrapperOrNull(long value, Class<?> dst) {
if (dst == int.class || dst == Integer.class)
return (int)(value);
if (dst == long.class || dst == Long.class)
return (long)(value);
if (dst == char.class || dst == Character.class)
return (char)(value);
if (dst == short.class || dst == Short.class)
return (short)(value);
if (dst == float.class || dst == Float.class)
return (float)(value);
if (dst == double.class || dst == Double.class)
return (double)(value);
if (dst == byte.class || dst == Byte.class)
return (byte)(value);
if (dst == boolean.class || dst == boolean.class)
return ((value % 29) & 1) == 0;
return null;
}
static final int ONE_MILLION = (1000*1000), // first int value
TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits
INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit;
static long nextArgVal;
static long nextArg(boolean moreBits) {
long val = nextArgVal++;
long sign = -(val & 1); // alternate signs
val >>= 1;
if (moreBits)
// Guarantee some bits in the high word.
// In any case keep the decimal representation simple-looking,
// with lots of zeroes, so as not to make the printed decimal
// strings unnecessarily noisy.
val += (val % ONE_MILLION) * TEN_BILLION;
return val ^ sign;
}
static int nextArg() {
// Produce a 32-bit result something like ONE_MILLION+(smallint).
// Example: 1_000_042.
return (int) nextArg(false);
}
static long nextArg(Class<?> kind) {
if (kind == long.class || kind == Long.class ||
kind == double.class || kind == Double.class)
// produce a 64-bit result something like
// ((TEN_BILLION+1) * (ONE_MILLION+(smallint)))
// Example: 10_000_420_001_000_042.
return nextArg(true);
return (long) nextArg();
}
static Object randomArg(Class<?> param) {
Object wrap = castToWrapperOrNull(nextArg(param), param);
if (wrap != null) {
return wrap;
}
// import sun.invoke.util.Wrapper;
// Wrapper wrap = Wrapper.forBasicType(dst);
// if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst))
// wrap = Wrapper.forWrapperType(dst);
// if (wrap != Wrapper.OBJECT)
// return wrap.wrap(nextArg++);
if (param.isInterface()) {
for (Class<?> c : param.getClasses()) {
if (param.isAssignableFrom(c) && !c.isInterface())
{ param = c; break; }
}
}
if (param.isInterface() || param.isAssignableFrom(String.class))
return "#"+nextArg();
else
try {
return param.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
}
return null; // random class not Object, String, Integer, etc.
}
static Object[] randomArgs(Class<?>... params) {
Object[] args = new Object[params.length];
for (int i = 0; i < args.length; i++)
args[i] = randomArg(params[i]);
return args;
}
static Object[] randomArgs(int nargs, Class<?> param) {
Object[] args = new Object[nargs];
for (int i = 0; i < args.length; i++)
args[i] = randomArg(param);
return args;
}
static final Object ANON_OBJ = new Object();
static Object zeroArg(Class<?> param) {
Object x = castToWrapperOrNull(0L, param);
if (x != null) return x;
if (param.isInterface() || param.isAssignableFrom(String.class)) return "\"\"";
if (param == Object.class) return ANON_OBJ;
if (param.getComponentType() != null) return Array.newInstance(param.getComponentType(), 0);
return null;
}
static Object[] zeroArgs(Class<?>... params) {
Object[] args = new Object[params.length];
for (int i = 0; i < args.length; i++)
args[i] = zeroArg(params[i]);
return args;
}
static Object[] zeroArgs(List<Class<?>> params) {
return zeroArgs(params.toArray(new Class<?>[0]));
}
@SafeVarargs @SuppressWarnings("varargs")
static <T, E extends T> T[] array(Class<T[]> atype, E... a) {
return Arrays.copyOf(a, a.length, atype);
}
@SafeVarargs @SuppressWarnings("varargs")
static <T> T[] cat(T[] a, T... b) {
int alen = a.length, blen = b.length;
if (blen == 0) return a;
T[] c = Arrays.copyOf(a, alen + blen);
System.arraycopy(b, 0, c, alen, blen);
return c;
}
static Integer[] boxAll(int... vx) {
Integer[] res = new Integer[vx.length];
for (int i = 0; i < res.length; i++) {
res[i] = vx[i];
}
return res;
}
static Object getClasses(Object x) {
if (x == null) return x;
if (x instanceof String) return x; // keep the name
if (x instanceof List) {
// recursively report classes of the list elements
Object[] xa = ((List)x).toArray();
for (int i = 0; i < xa.length; i++)
xa[i] = getClasses(xa[i]);
return Arrays.asList(xa);
}
return x.getClass().getSimpleName();
}
static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) {
return changeArgTypes(target, 0, 999, argType);
}
static MethodHandle changeArgTypes(MethodHandle target,
int beg, int end, Class<?> argType) {
MethodType targetType = target.type();
end = Math.min(end, targetType.parameterCount());
ArrayList<Class<?>> argTypes = new ArrayList<>(targetType.parameterList());
Collections.fill(argTypes.subList(beg, end), argType);
MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes);
return target.asType(ttype2);
}
// This lookup is good for all members in and under InvokeGenericTest.
static final Lookup LOOKUP = MethodHandles.lookup();
Map<List<Class<?>>, MethodHandle> CALLABLES = new HashMap<>();
MethodHandle callable(List<Class<?>> params) {
MethodHandle mh = CALLABLES.get(params);
if (mh == null) {
mh = collector_MH.asType(methodType(Object.class, params));
CALLABLES.put(params, mh);
}
return mh;
}
MethodHandle callable(Class<?>... params) {
return callable(Arrays.asList(params));
}
private static Object collector(Object... args) {
return Arrays.asList(args);
}
private static final MethodHandle collector_MH;
static {
try {
collector_MH
= LOOKUP.findStatic(LOOKUP.lookupClass(),
"collector",
methodType(Object.class, Object[].class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
@Test
public void testSimple() throws Throwable {
startTest("testSimple");
countTest();
String[] args = { "one", "two" };
MethodHandle mh = callable(Object.class, String.class);
Object res; List<?> resl;
res = resl = (List<?>) mh.invoke((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
}
@Test
public void testSimplePrims() throws Throwable {
startTest("testSimplePrims");
countTest();
int[] args = { 1, 2 };
MethodHandle mh = callable(Object.class, Object.class);
Object res; List<?> resl;
res = resl = (List<?>) mh.invoke(args[0], args[1]);
//System.out.println(res);
assertEquals(Arrays.toString(args), res.toString());
}
@Test
public void testAlternateName() throws Throwable {
startTest("testAlternateName");
countTest();
String[] args = { "one", "two" };
MethodHandle mh = callable(Object.class, String.class);
Object res; List<?> resl;
res = resl = (List<?>) mh.invoke((String)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
}
@Test
public void testWrongArgumentCount() throws Throwable {
startTest("testWrongArgumentCount");
for (int i = 0; i <= 10; i++) {
testWrongArgumentCount(Collections.<Class<?>>nCopies(i, Integer.class));
if (i <= 4) {
testWrongArgumentCount(Collections.<Class<?>>nCopies(i, int.class));
testWrongArgumentCount(Collections.<Class<?>>nCopies(i, long.class));
}
}
}
public void testWrongArgumentCount(List<Class<?>> params) throws Throwable {
int max = params.size();
for (int i = 0; i < max; i++) {
List<Class<?>> params2 = params.subList(0, i);
for (int k = 0; k <= 2; k++) {
if (k == 1) params = methodType(Object.class, params).generic().parameterList();
if (k == 2) params2 = methodType(Object.class, params2).generic().parameterList();
testWrongArgumentCount(params, params2);
testWrongArgumentCount(params2, params);
}
}
}
public void testWrongArgumentCount(List<Class<?>> expect, List<Class<?>> observe) throws Throwable {
countTest(false);
assertNotEquals(expect, observe);
MethodHandle target = callable(expect);
Object[] args = zeroArgs(observe);
Object junk;
try {
switch (args.length) {
case 0:
junk = target.invoke(); break;
case 1:
junk = target.invoke(args[0]); break;
case 2:
junk = target.invoke(args[0], args[1]); break;
case 3:
junk = target.invoke(args[0], args[1], args[2]); break;
case 4:
junk = target.invoke(args[0], args[1], args[2], args[3]); break;
default:
junk = target.invokeWithArguments(args); break;
}
} catch (WrongMethodTypeException ex) {
return;
} catch (Exception ex) {
throw new RuntimeException("wrong exception calling "+target+" on "+Arrays.asList(args), ex);
}
throw new RuntimeException("bad success calling "+target+" on "+Arrays.asList(args));
}
/** Make a list of all combinations of the given types, with the given arities.
* A void return type is possible iff the first type is void.class.
*/
static List<MethodType> allMethodTypes(int minargc, int maxargc, Class<?>... types) {
ArrayList<MethodType> result = new ArrayList<>();
if (types.length > 0) {
ArrayList<MethodType> argcTypes = new ArrayList<>();
// build arity-zero types first
for (Class<?> rtype : types) {
argcTypes.add(MethodType.methodType(rtype));
}
if (types[0] == void.class)
// void is not an argument type
types = Arrays.copyOfRange(types, 1, types.length);
for (int argc = 0; argc <= maxargc; argc++) {
if (argc >= minargc)
result.addAll(argcTypes);
if (argc >= maxargc)
break;
ArrayList<MethodType> prevTypes = argcTypes;
argcTypes = new ArrayList<>();
for (MethodType prevType : prevTypes) {
for (Class<?> ptype : types) {
argcTypes.add(prevType.insertParameterTypes(argc, ptype));
}
}
}
}
return Collections.unmodifiableList(result);
}
static List<MethodType> allMethodTypes(int argc, Class<?>... types) {
return allMethodTypes(argc, argc, types);
}
MethodHandle toString_MH;
@Test
public void testReferenceConversions() throws Throwable {
startTest("testReferenceConversions");
toString_MH = LOOKUP.
findVirtual(Object.class, "toString", MethodType.methodType(String.class));
Object[] args = { "one", "two" };
for (MethodType type : allMethodTypes(2, Object.class, String.class, CharSequence.class)) {
testReferenceConversions(type, args);
}
}
public void testReferenceConversions(MethodType type, Object... args) throws Throwable {
countTest();
int nargs = args.length;
List<Object> argList = Arrays.asList(args);
String expectString = argList.toString();
if (verbosity > 3) System.out.println("target type: "+type+expectString);
MethodHandle mh = callable(type.parameterList());
mh = MethodHandles.filterReturnValue(mh, toString_MH);
mh = mh.asType(type);
Object res = null;
assertEquals(2, nargs);
res = mh.invoke((Object)args[0], (Object)args[1]);
assertEquals(expectString, res);
res = mh.invoke((String)args[0], (Object)args[1]);
assertEquals(expectString, res);
res = mh.invoke((Object)args[0], (String)args[1]);
assertEquals(expectString, res);
res = mh.invoke((String)args[0], (String)args[1]);
assertEquals(expectString, res);
res = mh.invoke((String)args[0], (CharSequence)args[1]);
assertEquals(expectString, res);
res = mh.invoke((CharSequence)args[0], (Object)args[1]);
assertEquals(expectString, res);
res = (String) mh.invoke((Object)args[0], (Object)args[1]);
assertEquals(expectString, res);
res = (String) mh.invoke((String)args[0], (Object)args[1]);
assertEquals(expectString, res);
res = (CharSequence) mh.invoke((String)args[0], (Object)args[1]);
assertEquals(expectString, res);
//System.out.println(res);
}
@Test
public void testBoxConversions() throws Throwable {
startTest("testBoxConversions");
countTest();
Object[] args = { 1, 2 };
MethodHandle mh = callable(Object.class, int.class);
Object res; List<?> resl; int resi;
res = resl = (List<?>) mh.invoke((int)args[0], (Object)args[1]);
//System.out.println(res);
assertEquals(Arrays.asList(args), res);
mh = MethodHandles.identity(int.class);
mh = MethodHandles.dropArguments(mh, 1, int.class);
res = resi = (int) mh.invoke((Object) args[0], (Object) args[1]);
assertEquals(args[0], res);
res = resi = (int) mh.invoke((int) args[0], (Object) args[1]);
assertEquals(args[0], res);
}
}

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2017, 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 8157246
* @summary Tests invocation of MethodHandle with invalid leading argument
* @run junit/othervm test.java.lang.invoke.InvokeMethodHandleWithBadArgument
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import static java.lang.invoke.MethodType.methodType;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* Tests invocation of MethodHandle with invalid leading argument such as
* MethodHandle, VarHandle, and array object
*/
public class InvokeMethodHandleWithBadArgument {
// ---- null array reference ----
@Test
public void testAsSpreaderPosInvokeWithNull() throws Throwable {
MethodHandle spreader = MH_spread.asSpreader(1, int[].class, 3);
assertThrows(NullPointerException.class, () -> spreader.invoke("A", null, "B"));
}
@Test
public void testAsSpreaderInvokeWithNull() throws Throwable {
MethodHandle spreader = MH_String_equals.asSpreader(String[].class, 2);
assertTrue((boolean) spreader.invokeExact(new String[]{"me", "me"}));
assertThrows(NullPointerException.class, () -> {
boolean eq = (boolean) spreader.invokeExact((String[]) null);
});
}
// ---- incorrect array element count ----
@Test
public void testAsSpreaderPosInvokeWithBadElementCount() throws Throwable {
MethodHandle spreader = MH_spread.asSpreader(1, int[].class, 3);
assertThrows(IllegalArgumentException.class, () -> spreader.invoke("A", new int[]{1, 2}, "B"));
}
@Test
public void testAsSpreaderInvokeWithBadElementCount() throws Throwable {
MethodHandle spreader = MH_String_equals.asSpreader(String[].class, 2);
assertFalse((boolean) spreader.invokeExact(new String[]{"me", "thee"}));
assertThrows(IllegalArgumentException.class, () -> {
boolean eq = (boolean) spreader.invokeExact(new String[0]);
});
}
// ---- spread no argument ----
@Test
public void testAsSpreaderPosInvokeWithZeroLength() throws Throwable {
MethodHandle spreader = MH_spread.asSpreader(1, int[].class, 0);
assertEquals("A123B", spreader.invoke("A", (int[]) null, 1, 2, 3, "B"));
}
@Test
public void testAsSpreaderInvokeWithZeroLength() throws Throwable {
MethodHandle spreader = MH_String_equals.asSpreader(String[].class, 0);
assertTrue((boolean) spreader.invokeExact("me", (Object)"me", new String[0]));
boolean eq = (boolean) spreader.invokeExact("me", (Object)"me", (String[]) null);
}
// ---- invokers with null method/var handle argument ----
@Test
public void testInvokerWithNull() throws Throwable {
MethodType type = methodType(int.class, int.class, int.class);
MethodHandle invoker = MethodHandles.invoker(type);
assertEquals(3, (int) invoker.invoke(MH_add, 1, 2));
assertThrows(NullPointerException.class, () -> {
int sum = (int)invoker.invoke((MethodHandle)null, 1, 2);
});
}
@Test
public void testExactInvokerWithNull() throws Throwable {
MethodType type = methodType(int.class, int.class, int.class);
MethodHandle invoker = MethodHandles.exactInvoker(type);
assertEquals(3, (int) invoker.invoke(MH_add, 1, 2));
assertThrows(NullPointerException.class, () -> {
int sum = (int)invoker.invokeExact((MethodHandle)null, 1, 2);
});
}
@Test
public void testSpreadInvokerWithNull() throws Throwable {
MethodType type = methodType(boolean.class, String.class, String.class);
MethodHandle invoker = MethodHandles.spreadInvoker(type, 0);
assertTrue((boolean) invoker.invoke(MH_String_equals, new String[]{"me", "me"}));
assertThrows(NullPointerException.class, () -> {
boolean eq = (boolean) invoker.invoke((MethodHandle)null, new String[]{"me", "me"});
});
}
@Test
public void testVarHandleInvokerWithNull() throws Throwable {
VarHandle.AccessMode am = VarHandle.AccessMode.GET;
MethodHandle invoker = MethodHandles.varHandleInvoker(am, VH_array.accessModeType(am));
assertEquals(3, (int) invoker.invoke(VH_array, array, 3));
assertThrows(NullPointerException.class, () -> {
int value = (int)invoker.invoke((VarHandle)null, array, 3);
});
}
@Test
public void testVarHandleExactInvokerWithNull() throws Throwable {
VarHandle.AccessMode am = VarHandle.AccessMode.GET;
MethodHandle invoker = MethodHandles.varHandleExactInvoker(am, VH_array.accessModeType(am));
assertEquals(3, (int) invoker.invoke(VH_array, array, 3));
assertThrows(NullPointerException.class, () -> {
int value = (int)invoker.invokeExact((VarHandle)null, array, 3);
});
}
static final Lookup LOOKUP = MethodHandles.lookup();
static final MethodHandle MH_add;
static final MethodHandle MH_spread;
static final MethodHandle MH_String_equals;
static final VarHandle VH_array;
static final int[] array = new int[] { 0, 1, 2, 3, 4, 5};
static {
try {
Class<?> arrayClass = Class.forName("[I");
VH_array = MethodHandles.arrayElementVarHandle(arrayClass);
MH_add = LOOKUP.findStatic(InvokeMethodHandleWithBadArgument.class, "add",
methodType(int.class, int.class, int.class));
MH_spread = LOOKUP.findStatic(InvokeMethodHandleWithBadArgument.class, "spread",
methodType(String.class, String.class, int.class, int.class, int.class, String.class));
MH_String_equals = LOOKUP.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
static String spread(String s1, int i1, int i2, int i3, String s2) {
return s1 + i1 + i2 + i3 + s2;
}
static int add(int x, int y) {
return x+y;
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2017, 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
* @summary basic tests for MethodHandle.invokeWithArguments
* @run junit test.java.lang.invoke.InvokeWithArgumentsTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.WrongMethodTypeException;
import static java.lang.invoke.MethodType.methodType;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class InvokeWithArgumentsTest {
static final MethodHandles.Lookup L = MethodHandles.lookup();
static Object[] arity(Object o1, Object o2, Object... a) {
return a;
}
@Test
public void testArity() throws Throwable {
MethodHandle mh = L.findStatic(L.lookupClass(), "arity",
methodType(Object[].class, Object.class, Object.class, Object[].class));
assertThrows(WrongMethodTypeException.class, () -> mh.invokeWithArguments(""));
}
static Object[] passThrough(String... a) {
return a;
}
static Object[] pack(Object o, Object... a) {
return a;
}
@Test
public void testArrayNoPassThrough() throws Throwable {
String[] actual = {"A", "B"};
MethodHandle mh = L.findStatic(L.lookupClass(), "passThrough",
methodType(Object[].class, String[].class));
// Note: the actual array is not preserved, the elements will be
// unpacked and then packed into a new array before invoking the method
String[] result = (String[]) mh.invokeWithArguments(actual);
assertNotSame(actual, result, "Array should not pass through");
assertArrayEquals(actual, result, "Array contents should be equal");
}
@Test
public void testArrayPack() throws Throwable {
String[] actual = new String[]{"A", "B"};
MethodHandle mh = L.findStatic(L.lookupClass(), "pack",
methodType(Object[].class, Object.class, Object[].class));
// Note: since String[] can be cast to Object, the actual String[] array
// will cast to Object become the single element of a new Object[] array
Object[] expected = (Object[]) mh.invokeWithArguments("", actual);
assertEquals(1, expected.length, "Array should contain just one element");
assertSame(actual, expected[0], "Array should pass through");
}
static void intArray(int... a) {
}
@Test
public void testPrimitiveArrayWithNull() throws Throwable {
MethodHandle mh = L.findStatic(L.lookupClass(), "intArray",
methodType(void.class, int[].class));
assertThrows(NullPointerException.class, () -> mh.invokeWithArguments(null, null));
}
@Test
public void testPrimitiveArrayWithRef() throws Throwable {
MethodHandle mh = L.findStatic(L.lookupClass(), "intArray",
methodType(void.class, int[].class));
assertThrows(ClassCastException.class, () -> mh.invokeWithArguments("A", "B"));
}
static void numberArray(Number... a) {
}
@Test
public void testRefArrayWithCast() throws Throwable {
MethodHandle mh = L.findStatic(L.lookupClass(), "numberArray",
methodType(void.class, Number[].class));
// All numbers, should not throw
mh.invokeWithArguments(1, 1.0, 1.0F, 1L);
assertThrows(ClassCastException.class, () -> mh.invokeWithArguments("A"));
}
}

View file

@ -0,0 +1,952 @@
/*
* Copyright (c) 2009, 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 example code used in javadoc for java.lang.invoke API
* @compile JavaDocExamplesTest.java
* @run junit/othervm test.java.lang.invoke.JavaDocExamplesTest
*/
package test.java.lang.invoke;
import java.io.StringWriter;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import java.util.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* @author jrose
*/
public class JavaDocExamplesTest {
/** Wrapper for running the TestNG tests in this module.
* Put TestNG on the classpath!
*/
public static void main(String... ignore) throws Throwable {
new JavaDocExamplesTest().run();
}
public void run() throws Throwable {
testMisc();
testFindStatic();
testFindConstructor();
testFindVirtual();
testFindSpecial();
testPermuteArguments();
testZero();
testDropArguments();
testDropArgumentsToMatch();
testFilterArguments();
testFoldArguments();
testFoldArguments2();
testMethodHandlesSummary();
testAsSpreader();
testAsCollector();
testAsVarargsCollector();
testAsFixedArity();
testAsTypeCornerCases();
testMutableCallSite();
}
// How much output?
static final Class<?> THIS_CLASS = JavaDocExamplesTest.class;
static int verbosity = Integer.getInteger(THIS_CLASS.getSimpleName()+".verbosity", 0);
{}
private static final Lookup LOOKUP = lookup();
// static final private MethodHandle CONCAT_1 = LOOKUP.findVirtual(String.class,
// "concat", methodType(String.class, String.class));
// static final private MethodHandle HASHCODE_1 = LOOKUP.findVirtual(Object.class,
// "hashCode", methodType(int.class));
// form required if ReflectiveOperationException is intercepted:
private static final MethodHandle CONCAT_2, HASHCODE_2, ADD_2, SUB_2;
static {
try {
Class<?> THIS_CLASS = LOOKUP.lookupClass();
CONCAT_2 = LOOKUP.findVirtual(String.class,
"concat", methodType(String.class, String.class));
HASHCODE_2 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
ADD_2 = LOOKUP.findStatic(THIS_CLASS, "add", methodType(int.class, int.class, int.class));
SUB_2 = LOOKUP.findStatic(THIS_CLASS, "sub", methodType(int.class, int.class, int.class));
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
}
static int add(int x, int y) { return x + y; }
static int sub(int x, int y) { return x - y; }
{}
@Test public void testMisc() throws Throwable {
// Extra tests, not from javadoc:
{}
MethodHandle CONCAT_3 = LOOKUP.findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle HASHCODE_3 = LOOKUP.findVirtual(Object.class,
"hashCode", methodType(int.class));
//assertEquals("xy", (String) CONCAT_1.invokeExact("x", "y"));
assertEquals("xy", (String) CONCAT_2.invokeExact("x", "y"));
assertEquals("xy", (String) CONCAT_3.invokeExact("x", "y"));
//assertEquals("xy".hashCode(), (int) HASHCODE_1.invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) HASHCODE_2.invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) HASHCODE_3.invokeExact((Object)"xy"));
{}
}
@Test public void testFindStatic() throws Throwable {
{}
MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
"asList", methodType(List.class, Object[].class));
assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
{}
}
@Test public void testFindVirtual() throws Throwable {
{}
MethodHandle MH_concat = publicLookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
"hashCode", methodType(int.class));
MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
"hashCode", methodType(int.class));
assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
// interface method:
MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
"subSequence", methodType(CharSequence.class, int.class, int.class));
assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
// constructor "internal method" must be accessed differently:
MethodType MT_newString = methodType(void.class); //()V for new String()
try { assertEquals("impossible", lookup()
.findVirtual(String.class, "<init>", MT_newString));
} catch (NoSuchMethodException ex) { } // OK
MethodHandle MH_newString = publicLookup()
.findConstructor(String.class, MT_newString);
assertEquals("", (String) MH_newString.invokeExact());
{}
}
@Test public void testFindConstructor() throws Throwable {
{}
MethodHandle MH_newArrayList = publicLookup().findConstructor(
ArrayList.class, methodType(void.class, Collection.class));
Collection orig = Arrays.asList("x", "y");
Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
assert(orig != copy);
assertEquals(orig, copy);
// a variable-arity constructor:
MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
ProcessBuilder.class, methodType(void.class, String[].class));
ProcessBuilder pb = (ProcessBuilder)
MH_newProcessBuilder.invoke("x", "y", "z");
assertEquals("[x, y, z]", pb.command().toString());
{}
}
// for testFindSpecial
{}
static class Listie extends ArrayList {
public String toString() { return "[wee Listie]"; }
static Lookup lookup() { return MethodHandles.lookup(); }
}
{}
@Test public void testFindSpecial() throws Throwable {
{}
// no access to constructor via invokeSpecial:
MethodHandle MH_newListie = Listie.lookup()
.findConstructor(Listie.class, methodType(void.class));
Listie l = (Listie) MH_newListie.invokeExact();
try { assertEquals("impossible", Listie.lookup().findSpecial(
Listie.class, "<init>", methodType(void.class), Listie.class));
} catch (NoSuchMethodException ex) { } // OK
// access to super and self methods via invokeSpecial:
MethodHandle MH_super = Listie.lookup().findSpecial(
ArrayList.class, "toString" , methodType(String.class), Listie.class);
MethodHandle MH_this = Listie.lookup().findSpecial(
Listie.class, "toString" , methodType(String.class), Listie.class);
MethodHandle MH_duper = Listie.lookup().findSpecial(
Object.class, "toString" , methodType(String.class), Listie.class);
assertEquals("[]", (String) MH_super.invokeExact(l));
assertEquals(""+l, (String) MH_this.invokeExact(l));
assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
try { assertEquals("inaccessible", Listie.lookup().findSpecial(
String.class, "toString", methodType(String.class), Listie.class));
} catch (IllegalAccessException ex) { } // OK
Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
{}
}
@Test public void testPermuteArguments() throws Throwable {
{{
{} /// JAVADOC
MethodType intfn1 = methodType(int.class, int.class);
MethodType intfn2 = methodType(int.class, int.class, int.class);
MethodHandle sub = SUB_2;// ... {int x, int y => x-y} ...;
assert(sub.type().equals(intfn2));
MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
assert((int)rsub.invokeExact(1, 100) == 99);
MethodHandle add = ADD_2;// ... {int x, int y => x+y} ...;
assert(add.type().equals(intfn2));
MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
assert(twice.type().equals(intfn1));
assert((int)twice.invokeExact(21) == 42);
}}
{{
{} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodHandle d0 = dropArguments(cat, 0, String.class);
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
MethodHandle d1 = dropArguments(cat, 1, String.class);
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
MethodHandle d2 = dropArguments(cat, 2, String.class);
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
}}
}
@Test public void testZero() throws Throwable {
{{
{} /// JAVADOC
Class<?> type = Double.class;
MethodHandle mh1 = MethodHandles.explicitCastArguments(MethodHandles.constant(Object.class, null), methodType(type));
assertEquals("()Double", mh1.type().toString());
MethodHandle mh2 = MethodHandles.empty(methodType(type));
assertEquals("()Double", mh2.type().toString());
}}
}
@Test public void testDropArguments() throws Throwable {
{{
{} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
assertEquals(bigType, d0.type());
assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
}}
{{
{} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodHandle d0 = dropArguments(cat, 0, String.class);
assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
MethodHandle d1 = dropArguments(cat, 1, String.class);
assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
MethodHandle d2 = dropArguments(cat, 2, String.class);
assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
}}
}
@Test public void testDropArgumentsToMatch() throws Throwable {
{{
{} /// JAVADOC
MethodHandle h0= constant(boolean.class, true);
MethodHandle h1 = lookup().findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodType bigType = h1.type().insertParameterTypes(1, String.class, int.class);
MethodHandle h2 = dropArguments(h1, 0, bigType.parameterList());
if (h1.type().parameterCount() < h2.type().parameterCount()) {
h1 = dropArgumentsToMatch(h1, 0, h2.type().parameterList(), 0); // lengthen h1
}
else {
h2 = dropArgumentsToMatch(h2, 0, h1.type().parameterList(), 0); // lengthen h2
}
MethodHandle h3 = guardWithTest(h0, h1, h2);
assertEquals("xy", h3.invoke("x", "y", 1, "a", "b", "c"));
}}
}
@Test public void testFilterArguments() throws Throwable {
{{
{} /// JAVADOC
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle upcase = lookup().findVirtual(String.class,
"toUpperCase", methodType(String.class));
assertEquals("xy", (String) cat.invokeExact("x", "y"));
MethodHandle f0 = filterArguments(cat, 0, upcase);
assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
MethodHandle f1 = filterArguments(cat, 1, upcase);
assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
}}
}
@Test public void testCollectArguments() throws Throwable {
{{
{} /// JAVADOC
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
assertEquals("[strange]", (String) ts1.invokeExact("strange"));
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
assertEquals("[top, [up, down], strange]",
(String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
assertEquals("[top, [up, down], [strange]]",
(String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
assertEquals("[top, [[up, down, strange], charm], bottom]",
(String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
}}
}
@Test public void testFoldArguments() throws Throwable {
{{
{} /// JAVADOC
MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
"println", methodType(void.class, String.class))
.bindTo(System.out);
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
MethodHandle catTrace = foldArguments(cat, trace);
// also prints "boo":
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
}}
}
static void assertEquals(Object exp, Object act) {
if (verbosity > 0)
System.out.println("result: "+act);
Assertions.assertEquals(exp, act);
}
static void assertTrue(boolean b) {
if (verbosity > 0) {
System.out.println("result: " + b);
}
Assertions.assertTrue(b);
}
@Test public void testMethodHandlesSummary() throws Throwable {
{{
{} /// JAVADOC
Object x, y; String s; int i;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invokeExact("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s, "nanny");
// weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
assertEquals(s, "savvy");
// mt is (Object[])List
mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
x = mh.invoke("one", "two");
// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList("one","two"));
// mt is (Object,Object,Object)Object
mt = MethodType.genericMethodType(3);
mh = mh.asType(mt);
x = mh.invokeExact((Object)1, (Object)2, (Object)3);
// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
assertEquals(x, java.util.Arrays.asList(1,2,3));
// mt is ()int
mt = MethodType.methodType(int.class);
mh = lookup.findVirtual(java.util.List.class, "size", mt);
i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
// invokeExact(Ljava/util/List;)I
assert(i == 3);
mt = MethodType.methodType(void.class, String.class);
mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
mh.invokeExact(System.out, "Hello, world.");
// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
{}
}}
}
@Test public void testAsSpreader() throws Throwable {
{{
{} /// JAVADOC
MethodHandle equals = publicLookup()
.findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
assert( (boolean) equals.invokeExact("me", (Object)"me"));
assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
// spread both arguments from a 2-array:
MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
// try to spread from anything but a 2-array:
for (int n = 0; n <= 10; n++) {
Object[] badArityArgs = (n == 2 ? new Object[0] : new Object[n]);
try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
catch (IllegalArgumentException ex) { } // OK
}
// spread both arguments from a String array:
MethodHandle eq2s = equals.asSpreader(String[].class, 2);
assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
// spread second arguments from a 1-array:
MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
// spread no arguments from a 0-array or null:
MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
// asSpreader and asCollector are approximate inverses:
for (int n = 0; n <= 2; n++) {
for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
assert( (boolean) equals2.invokeWithArguments("me", "me"));
assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
}
}
MethodHandle caToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
MethodHandle caString3 = caToString.asCollector(char[].class, 3);
assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
}}
}
@Test public void testAsCollector() throws Throwable {
{{
{} /// JAVADOC
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
assertEquals("[won]", (String) deepToString.invokeExact(new Object[]{"won"}));
MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
assertEquals(methodType(String.class, Object.class), ts1.type());
//assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"})); //FAIL
assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
// arrayType can be a subtype of Object[]
MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
assertEquals(methodType(String.class, String.class, String.class), ts2.type());
assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
assertEquals("[]", (String) ts0.invokeExact());
// collectors can be nested, Lisp-style
MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
// arrayType can be any primitive array type
MethodHandle bytesToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
.asCollector(byte[].class, 3);
assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
MethodHandle longsToString = publicLookup()
.findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
.asCollector(long[].class, 1);
assertEquals("[123]", (String) longsToString.invokeExact((long)123));
}}
}
@SuppressWarnings("rawtypes")
@Test public void testAsVarargsCollector() throws Throwable {
{{
{} /// JAVADOC
MethodHandle deepToString = publicLookup()
.findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
assertEquals("[won]", (String) ts1.invokeExact( new Object[]{"won"}));
assertEquals("[won]", (String) ts1.invoke( new Object[]{"won"}));
assertEquals("[won]", (String) ts1.invoke( "won" ));
assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
// findStatic of Arrays.asList(...) produces a variable arity method handle:
MethodHandle asList = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
assertEquals(methodType(List.class, Object[].class), asList.type());
assert(asList.isVarargsCollector());
assertEquals("[]", asList.invoke().toString());
assertEquals("[1]", asList.invoke(1).toString());
assertEquals("[two, too]", asList.invoke("two", "too").toString());
String[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
List ls = (List) asList.invoke((Object)argv);
assertEquals(1, ls.size());
assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
}}
}
@Test public void testAsFixedArity() throws Throwable {
{{
{} /// JAVADOC
MethodHandle asListVar = publicLookup()
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
.asVarargsCollector(Object[].class);
MethodHandle asListFix = asListVar.asFixedArity();
assertEquals("[1]", asListVar.invoke(1).toString());
Exception caught = null;
try { asListFix.invoke((Object)1); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof ClassCastException);
assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
try { asListFix.invoke("two", "too"); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof WrongMethodTypeException);
Object[] argv = { "three", "thee", "tee" };
assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
}}
}
@Test public void testAsTypeCornerCases() throws Throwable {
{{
{} /// JAVADOC
MethodHandle i2s = publicLookup()
.findVirtual(Integer.class, "toString", methodType(String.class));
i2s = i2s.asType(i2s.type().unwrap());
MethodHandle l2s = publicLookup()
.findVirtual(Long.class, "toString", methodType(String.class));
l2s = l2s.asType(l2s.type().unwrap());
Exception caught = null;
try { i2s.asType(methodType(String.class, String.class)); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof WrongMethodTypeException);
i2s.asType(methodType(String.class, byte.class));
i2s.asType(methodType(String.class, Byte.class));
i2s.asType(methodType(String.class, Character.class));
i2s.asType(methodType(String.class, Integer.class));
l2s.asType(methodType(String.class, byte.class));
l2s.asType(methodType(String.class, Byte.class));
l2s.asType(methodType(String.class, Character.class));
l2s.asType(methodType(String.class, Integer.class));
l2s.asType(methodType(String.class, Long.class));
caught = null;
try { i2s.asType(methodType(String.class, Long.class)); }
catch (Exception ex) { caught = ex; }
assert(caught instanceof WrongMethodTypeException);
MethodHandle i2sGen = i2s.asType(methodType(String.class, Object.class));
MethodHandle l2sGen = l2s.asType(methodType(String.class, Object.class));
i2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int
i2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int
l2sGen.invoke(42); // int -> Integer -> Object -> Integer -> int
l2sGen.invoke((byte)4); // byte -> Byte -> Object -> Byte -> byte -> int
l2sGen.invoke(0x420000000L);
caught = null;
try { i2sGen.invoke(0x420000000L); } // long -> Long -> Object -> Integer CCE
catch (Exception ex) { caught = ex; }
assert(caught instanceof ClassCastException);
caught = null;
try { i2sGen.invoke("asdf"); } // String -> Object -> Integer CCE
catch (Exception ex) { caught = ex; }
assert(caught instanceof ClassCastException);
{}
}}
}
@Test public void testMutableCallSite() throws Throwable {
{{
{} /// JAVADOC
MutableCallSite name = new MutableCallSite(MethodType.methodType(String.class));
MethodHandle MH_name = name.dynamicInvoker();
MethodType MT_str1 = MethodType.methodType(String.class);
MethodHandle MH_upcase = MethodHandles.lookup()
.findVirtual(String.class, "toUpperCase", MT_str1);
MethodHandle worker1 = MethodHandles.filterReturnValue(MH_name, MH_upcase);
name.setTarget(MethodHandles.constant(String.class, "Rocky"));
assertEquals("ROCKY", (String) worker1.invokeExact());
name.setTarget(MethodHandles.constant(String.class, "Fred"));
assertEquals("FRED", (String) worker1.invokeExact());
// (mutation can be continued indefinitely)
/*
* </pre></blockquote>
* <p>
* The same call site may be used in several places at once.
* <blockquote><pre>
*/
MethodType MT_str2 = MethodType.methodType(String.class, String.class);
MethodHandle MH_cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
MethodHandle MH_dear = MethodHandles.insertArguments(MH_cat, 1, ", dear?");
MethodHandle worker2 = MethodHandles.filterReturnValue(MH_name, MH_dear);
assertEquals("Fred, dear?", (String) worker2.invokeExact());
name.setTarget(MethodHandles.constant(String.class, "Wilma"));
assertEquals("WILMA", (String) worker1.invokeExact());
assertEquals("Wilma, dear?", (String) worker2.invokeExact());
{}
}}
}
@Test public void testSwitchPoint() throws Throwable {
{{
{} /// JAVADOC
MethodHandle MH_strcat = MethodHandles.lookup()
.findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
SwitchPoint spt = new SwitchPoint();
assert(!spt.hasBeenInvalidated());
// the following steps may be repeated to re-use the same switch point:
MethodHandle worker1 = MH_strcat;
MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
MethodHandle worker = spt.guardWithTest(worker1, worker2);
assertEquals("method", (String) worker.invokeExact("met", "hod"));
SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
assert(spt.hasBeenInvalidated());
assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
{}
}}
}
@Test public void testFoldArguments2() throws Throwable {
{{
{} /// JAVADOC
// argument-based dispatch for methods of the form boolean x.___(y: String)
Lookup lookup = publicLookup();
// first, a tracing hack:
MethodHandle println = lookup.findVirtual(java.io.PrintStream.class, "println", methodType(void.class, String.class));
MethodHandle arrayToString = lookup.findStatic(Arrays.class, "toString", methodType(String.class, Object[].class));
MethodHandle concat = lookup.findVirtual(String.class, "concat", methodType(String.class, String.class));
MethodHandle arrayToString_DIS = filterReturnValue(arrayToString, concat.bindTo("DIS:"));
MethodHandle arrayToString_INV = filterReturnValue(arrayToString, concat.bindTo("INV:"));
MethodHandle printArgs_DIS = filterReturnValue(arrayToString_DIS, println.bindTo(System.out)).asVarargsCollector(Object[].class);
MethodHandle printArgs_INV = filterReturnValue(arrayToString_INV, println.bindTo(System.out)).asVarargsCollector(Object[].class);
// metaobject protocol:
MethodType mtype = methodType(boolean.class, String.class);
MethodHandle findVirtual = lookup.findVirtual(Lookup.class,
"findVirtual", methodType(MethodHandle.class, Class.class, String.class, MethodType.class));
MethodHandle getClass = lookup.findVirtual(Object.class,
"getClass", methodType(Class.class));
MethodHandle dispatch = findVirtual;
dispatch = filterArguments(dispatch, 1, getClass);
dispatch = insertArguments(dispatch, 3, mtype);
dispatch = dispatch.bindTo(lookup);
assertEquals(methodType(MethodHandle.class, Object.class, String.class), dispatch.type());
MethodHandle invoker = invoker(mtype.insertParameterTypes(0, Object.class));
// wrap tracing around the dispatch and invoke steps:
dispatch = foldArguments(dispatch, printArgs_DIS.asType(dispatch.type().changeReturnType(void.class)));
invoker = foldArguments(invoker, printArgs_INV.asType(invoker.type().changeReturnType(void.class)));
invoker = dropArguments(invoker, 2, String.class); // ignore selector
// compose the dispatcher and the invoker:
MethodHandle invokeDispatched = foldArguments(invoker, dispatch);
Object x = "football", y = new java.util.Scanner("bar");
assert( (boolean) invokeDispatched.invokeExact(x, "startsWith", "foo"));
assert(!(boolean) invokeDispatched.invokeExact(x, "startsWith", "#"));
assert( (boolean) invokeDispatched.invokeExact(x, "endsWith", "all"));
assert(!(boolean) invokeDispatched.invokeExact(x, "endsWith", "foo"));
assert( (boolean) invokeDispatched.invokeExact(y, "hasNext", "[abc]+[rst]"));
assert(!(boolean) invokeDispatched.invokeExact(y, "hasNext", "[123]+[789]"));
}}
}
static int one(int k) { return 1; }
static int inc(int i, int acc, int k) { return i + 1; }
static int mult(int i, int acc, int k) { return i * acc; }
static boolean pred(int i, int acc, int k) { return i < k; }
static int fin(int i, int acc, int k) { return acc; }
@Test public void testLoop() throws Throwable {
MethodHandle MH_inc, MH_one, MH_mult, MH_pred, MH_fin;
Class<?> I = int.class;
MH_inc = LOOKUP.findStatic(THIS_CLASS, "inc", methodType(I, I, I, I));
MH_one = LOOKUP.findStatic(THIS_CLASS, "one", methodType(I, I));
MH_mult = LOOKUP.findStatic(THIS_CLASS, "mult", methodType(I, I, I, I));
MH_pred = LOOKUP.findStatic(THIS_CLASS, "pred", methodType(boolean.class, I, I, I));
MH_fin = LOOKUP.findStatic(THIS_CLASS, "fin", methodType(I, I, I, I));
{{
{} /// JAVADOC
// iterative implementation of the factorial function as a loop handle
// null initializer for counter, should initialize to 0
MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
assertEquals(120, loop.invoke(5));
{}
}}
}
static int inc(int i) { return i + 1; } // drop acc, k
static int mult(int i, int acc) { return i * acc; } //drop k
static boolean cmp(int i, int k) { return i < k; }
@Test public void testSimplerLoop() throws Throwable {
MethodHandle MH_inc, MH_mult, MH_cmp;
Class<?> I = int.class;
MH_inc = LOOKUP.findStatic(THIS_CLASS, "inc", methodType(I, I));
MH_mult = LOOKUP.findStatic(THIS_CLASS, "mult", methodType(I, I, I));
MH_cmp = LOOKUP.findStatic(THIS_CLASS, "cmp", methodType(boolean.class, I, I));
{{
{} /// JAVADOC
// simplified implementation of the factorial function as a loop handle
// null initializer for counter, should initialize to 0
MethodHandle MH_one = MethodHandles.constant(int.class, 1);
MethodHandle MH_pred = MethodHandles.dropArguments(MH_cmp, 1, int.class); // drop acc
MethodHandle MH_fin = MethodHandles.dropArguments(MethodHandles.identity(int.class), 0, int.class); // drop i
MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
assertEquals(720, loop.invoke(6));
{}
}}
}
// for testFacLoop
{}
static class FacLoop {
final int k;
FacLoop(int k) { this.k = k; }
int inc(int i) { return i + 1; }
int mult(int i, int acc) { return i * acc; }
boolean pred(int i) { return i < k; }
int fin(int i, int acc) { return acc; }
}
{}
// assume MH_inc, MH_mult, and MH_pred are handles to the above methods
@Test public void testFacLoop() throws Throwable {
MethodHandle MH_FacLoop, MH_inc, MH_mult, MH_pred, MH_fin;
Class<?> I = int.class;
MH_FacLoop = LOOKUP.findConstructor(FacLoop.class, methodType(void.class, I));
MH_inc = LOOKUP.findVirtual(FacLoop.class, "inc", methodType(I, I));
MH_mult = LOOKUP.findVirtual(FacLoop.class, "mult", methodType(I, I, I));
MH_pred = LOOKUP.findVirtual(FacLoop.class, "pred", methodType(boolean.class, I));
MH_fin = LOOKUP.findVirtual(FacLoop.class, "fin", methodType(I, I, I));
{{
{} /// JAVADOC
// instance-based implementation of the factorial function as a loop handle
// null initializer for counter, should initialize to 0
MethodHandle MH_one = MethodHandles.constant(int.class, 1);
MethodHandle[] instanceClause = new MethodHandle[]{MH_FacLoop};
MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
MethodHandle loop = MethodHandles.loop(instanceClause, counterClause, accumulatorClause);
assertEquals(5040, loop.invoke(7));
{}
}}
}
static List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
zip.add(a.next());
zip.add(b.next());
return zip;
}
@Test public void testWhileLoop() throws Throwable {
MethodHandle MH_initZip, MH_zipPred, MH_zipStep;
Class<?> IT = Iterator.class;
Class<?> L = List.class;
MH_initZip = LOOKUP.findStatic(THIS_CLASS, "initZip", methodType(L, IT, IT));
MH_zipPred = LOOKUP.findStatic(THIS_CLASS, "zipPred", methodType(boolean.class, L, IT, IT));
MH_zipStep = LOOKUP.findStatic(THIS_CLASS, "zipStep", methodType(L, L, IT, IT));
{{
{} /// JAVADOC
// implement the zip function for lists as a loop handle
MethodHandle loop = MethodHandles.whileLoop(MH_initZip, MH_zipPred, MH_zipStep);
List<String> a = Arrays.asList("a", "b", "c", "d");
List<String> b = Arrays.asList("e", "f", "g", "h");
List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
{}
}}
}
static int zero(int limit) { return 0; }
static int step(int i, int limit) { return i + 1; }
static boolean pred(int i, int limit) { return i < limit; }
@Test public void testDoWhileLoop() throws Throwable {
MethodHandle MH_zero, MH_step, MH_pred;
Class<?> I = int.class;
MH_zero = LOOKUP.findStatic(THIS_CLASS, "zero", methodType(I, I));
MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(I, I, I));
MH_pred = LOOKUP.findStatic(THIS_CLASS, "pred", methodType(boolean.class, I, I));
{{
{} /// JAVADOC
// int i = 0; while (i < limit) { ++i; } return i; => limit
MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
assertEquals(23, loop.invoke(23));
{}
}}
}
static String step(String v, int counter, String start_) { return "na " + v; } //#0
static String step(String v, int counter ) { return "na " + v; } //#1
static String step(String v, int counter, int iterations_, String pre, String start_) { return pre + " " + v; } //#2
static String step3(String v, int counter, String pre) { return pre + " " + v; } //#3
@Test public void testCountedLoop() throws Throwable {
MethodHandle MH_step;
Class<?> S = String.class, I = int.class;
// Theme:
MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, S));
{{
{} /// JAVADOC
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
// => a variation on a well known theme
MethodHandle fit13 = MethodHandles.constant(int.class, 13);
MethodHandle start = MethodHandles.identity(String.class);
MethodHandle loop = MethodHandles.countedLoop(fit13, start, MH_step); // (v, i, _) -> "na " + v
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
{}
}}
// Variation #1:
MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I));
{{
{} /// JAVADOC
// String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
// => a variation on a well known theme
MethodHandle count = MethodHandles.dropArguments(MethodHandles.identity(int.class), 1, String.class);
MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class);
MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i) -> "na " + v
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "Lambdaman!"));
{}
assertEquals("na na Lambdaman!", loop.invoke(2, "Lambdaman!"));
assertEquals("Lambdaman!", loop.invoke(0, "Lambdaman!"));
assertEquals("Lambdaman!", loop.invoke(-1, "Lambdaman!"));
assertEquals("Lambdaman!", loop.invoke(Integer.MIN_VALUE, "Lambdaman!"));
}}
// Variation #2:
MH_step = LOOKUP.findStatic(THIS_CLASS, "step", methodType(S, S, I, I, S, S));
{{
{} /// JAVADOC
// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
// => a variation on a well known theme
MethodHandle count = MethodHandles.identity(int.class);
MethodHandle start = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0, int.class, String.class);
MethodHandle loop = MethodHandles.countedLoop(count, start, MH_step); // (v, i, _, pre, _) -> pre + " " + v
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke(13, "na", "Lambdaman!"));
{}
}}
// Variation #3:
MH_step = LOOKUP.findStatic(THIS_CLASS, "step3", methodType(S, S, I, S));
{{
{} /// JAVADOC
// String s = "Lambdaman!", t = "na"; for (int i = 0; i < 13; ++i) { s = t + " " + s; } return s;
// => a variation on a well known theme
MethodType loopType = methodType(String.class, String.class, int.class, String.class);
MethodHandle count = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(int.class), 0, loopType.parameterList(), 1);
MethodHandle start = MethodHandles.dropArgumentsToMatch(MethodHandles.identity(String.class), 0, loopType.parameterList(), 2);
MethodHandle body = MethodHandles.dropArgumentsToMatch(MH_step, 2, loopType.parameterList(), 0);
MethodHandle loop = MethodHandles.countedLoop(count, start, body); // (v, i, pre, _, _) -> pre + " " + v
assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("na", 13, "Lambdaman!"));
{}
}}
}
static List<String> reverseStep(List<String> r, String e) {
r.add(0, e);
return r;
}
static List<String> newArrayList() { return new ArrayList<>(); }
@Test public void testIteratedLoop() throws Throwable {
MethodHandle MH_newArrayList, MH_reverseStep;
Class<?> L = List.class, S = String.class;
MH_newArrayList = LOOKUP.findStatic(THIS_CLASS, "newArrayList", methodType(L));
MH_reverseStep = LOOKUP.findStatic(THIS_CLASS, "reverseStep", methodType(L, L, S));
{{
{} /// JAVADOC
// reverse a list
MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
assertEquals(reversedList, (List<String>) loop.invoke(list));
{}
}}
}
@Test public void testFoldArguments3() throws Throwable {
{{
{} /// JAVADOC
MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
"println", methodType(void.class, String.class))
.bindTo(System.out);
MethodHandle cat = lookup().findVirtual(String.class,
"concat", methodType(String.class, String.class));
assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
MethodHandle catTrace = foldArguments(cat, 1, trace);
// also prints "jum":
assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
{}
}}
}
@Test public void testAsCollector2() throws Throwable {
{{
{} /// JAVADOC
StringWriter swr = new StringWriter();
MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
assertEquals("BC", swr.toString());
swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
assertEquals("BCPQRS", swr.toString());
swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
assertEquals("BCPQRSZ", swr.toString());
{}
}}
}
@Test public void testAsSpreader2() throws Throwable {
{{
{} /// JAVADOC
MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
Object[] ints = new Object[]{3, 9, 7, 7};
Comparator<Integer> cmp = (a, b) -> a - b;
assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
{}
}}
}
/* ---- TEMPLATE ----
@Test public void testFoo() throws Throwable {
{{
{} /// JAVADOC
{}
}}
}
*/
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 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 Tests that Lookup can be produced from classes under java.util.concurrent
* @bug 8154447
* @compile/module=java.base java/util/concurrent/LookupTester.java
* @run junit/othervm JavaUtilConcurrentLookupTest
*/
import java.util.concurrent.LookupTester;
import org.junit.jupiter.api.Test;
public class JavaUtilConcurrentLookupTest {
@Test
public void testLookup() {
LookupTester.getLookup();
}
@Test
public void testLookupIn() {
LookupTester.getLookupIn();
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2014, 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.MethodHandle;
import java.lang.reflect.InvocationTargetException;
/**
* Abstract class for lambda forms caching testing.
*
* @author kshefov
*/
public abstract class LFCachingTestCase extends LambdaFormTestCase {
/**
* Constructor for lambda forms caching test case.
*
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
* returns a {@code j.l.i.MethodHandle} instance.
*/
protected LFCachingTestCase(TestMethods testMethod) {
super(testMethod);
}
/**
* Checks that the lambda forms of the two adapter method handles adapter1
* and adapter2 are the same.
*
* @param adapter1 First method handle.
* @param adapter2 Second method handle.
*/
public void checkLFCaching(MethodHandle adapter1, MethodHandle adapter2) {
try {
if (!adapter1.type().equals(adapter2.type())) {
throw new Error("TESTBUG: Types of the two method handles are not the same");
}
Object lambdaForm0 = LambdaFormTestCase.INTERNAL_FORM.invoke(adapter1);
Object lambdaForm1 = LambdaFormTestCase.INTERNAL_FORM.invoke(adapter2);
if (lambdaForm0 == null || lambdaForm1 == null) {
throw new Error("Unexpected error: One or both lambda forms of the method handles are null");
}
if (lambdaForm0 != lambdaForm1) {
// Since LambdaForm caches are based on SoftReferences, GC can cause element eviction.
if (noGCHappened()) {
System.err.println("Lambda form 0 toString is:");
System.err.println(lambdaForm0);
System.err.println("Lambda form 1 toString is:");
System.err.println(lambdaForm1);
throw new AssertionError("Error: Lambda forms of the two method handles"
+ " are not the same. LF cahing does not work");
} else {
System.err.println("LambdaForms differ, but there was a GC in between. Ignore the failure.");
}
}
} catch (IllegalAccessException | IllegalArgumentException |
SecurityException | InvocationTargetException ex) {
throw new Error("Unexpected exception", ex);
}
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2014, 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 LFGarbageCollectedTest
* @bug 8046703
* @key randomness
* @library /lib/testlibrary /java/lang/invoke/common
* @summary Test verifies that lambda forms are garbage collected
* @author kshefov
* @build jdk.test.lib.TimeLimitedRunner
* @build TestMethods
* @build LambdaFormTestCase
* @build LFGarbageCollectedTest
* @run main/othervm -Xmx64m
* -XX:SoftRefLRUPolicyMSPerMB=0
* -XX:+HeapDumpOnOutOfMemoryError
* -DHEAP_DUMP=false
* LFGarbageCollectedTest
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.EnumSet;
import java.util.Map;
/**
* Lambda forms garbage collection test class.
*/
public final class LFGarbageCollectedTest extends LambdaFormTestCase {
private static boolean HEAP_DUMP = Boolean.getBoolean("HEAP_DUMP");
/**
* Constructor for a lambda forms garbage collection test case.
*
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
* returns a {@code j.l.i.MethodHandle} instance.
*/
public LFGarbageCollectedTest(TestMethods testMethod) {
super(testMethod);
}
PhantomReference ph;
ReferenceQueue rq = new ReferenceQueue();
MethodType mtype;
Map<String, Object> data;
@Override
public void doTest() {
try {
TestMethods testCase = getTestMethod();
data = testCase.getTestCaseData();
MethodHandle adapter;
try {
adapter = testCase.getTestCaseMH(data, TestMethods.Kind.ONE);
} catch (NoSuchMethodException ex) {
throw new Error("Unexpected exception", ex);
}
mtype = adapter.type();
Object lambdaForm = INTERNAL_FORM.invoke(adapter);
if (lambdaForm == null) {
throw new Error("Unexpected error: Lambda form of the method handle is null");
}
String kind = KIND_FIELD.get(lambdaForm).toString();
if (kind.equals("IDENTITY")) {
// Ignore identity LambdaForms.
return;
}
ph = new PhantomReference(lambdaForm, rq);
lambdaForm = null;
adapter = null;
collectLambdaForm();
} catch (IllegalAccessException | IllegalArgumentException |
InvocationTargetException ex) {
throw new Error("Unexpected exception", ex);
}
}
private void collectLambdaForm() throws IllegalAccessException {
// Usually, 2 System.GCs are necessary to enqueue a SoftReference.
System.gc();
System.gc();
Reference ref = null;
for (int i = 0; i < 10; i++) {
try {
ref = rq.remove(1000);
} catch (InterruptedException e) {
/* ignore */
}
if (ref != null) {
break;
}
System.gc(); // If the reference hasn't been queued yet, trigger one more GC.
}
if (ref == null) {
dumpTestData();
System.err.println("Method type: " + mtype);
System.err.println("LambdaForm: " + REF_FIELD.get(ph));
if (HEAP_DUMP) {
// Trigger OOM to force heap dump for post-mortem analysis.
val = new long[1_000_000_000];
}
throw new AssertionError("Error: LambdaForm is not garbage collected");
};
}
private void dumpTestData() {
System.err.println("Test case: " + getTestMethod());
for (String s : data.keySet()) {
System.err.printf("\t%20s => %s\n", s, data.get(s));
}
}
private static long[] val;
/**
* Main routine for lambda forms garbage collection test.
*
* @param args Accepts no arguments.
*/
public static void main(String[] args) {
// The "identity", "constant", "arrayElementGetter" and "arrayElementSetter"
// methods should be removed from this test,
// because their lambda forms are stored in a static field and are not GC'ed.
// There can be only a finite number of such LFs for each method,
// so no memory leak happens.
EnumSet<TestMethods> testMethods = EnumSet.complementOf(EnumSet.of(
TestMethods.IDENTITY,
TestMethods.CONSTANT,
TestMethods.ARRAY_ELEMENT_GETTER,
TestMethods.ARRAY_ELEMENT_SETTER,
TestMethods.EXACT_INVOKER,
TestMethods.INVOKER));
LambdaFormTestCase.runTests(LFGarbageCollectedTest::new, testMethods);
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) 2014, 2017, 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 LFMultiThreadCachingTest
* @bug 8046703
* @key randomness
* @summary Test verifies that lambda forms are cached when run with multiple threads
* @author kshefov
* @library /lib/testlibrary /java/lang/invoke/common /test/lib
* @modules java.base/java.lang.invoke:open
* java.base/java.lang.ref:open
* java.management
* @build jdk.test.lib.TimeLimitedRunner
* @build TestMethods
* @build LambdaFormTestCase
* @build LFCachingTestCase
* @build LFMultiThreadCachingTest
* @run main/othervm LFMultiThreadCachingTest
*/
import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
import java.lang.invoke.MethodHandle;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
/**
* Multiple threaded lambda forms caching test class.
*/
public final class LFMultiThreadCachingTest extends LFCachingTestCase {
private static final TestMethods.Kind[] KINDS;
static {
EnumSet<TestMethods.Kind> set
= EnumSet.complementOf(EnumSet.of(TestMethods.Kind.EXCEPT));
KINDS = set.toArray(new TestMethods.Kind[set.size()]);
if (KINDS.length < 2) {
throw new Error("TESTBUG: KINDS.length[" + KINDS.length
+ "] should be at least 2");
}
}
private static final int CORES
= Math.max(KINDS.length, Runtime.getRuntime().availableProcessors());
/**
* Constructor a for multiple threaded lambda forms caching test case.
*
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
* returns a {@code j.l.i.MethodHandle} instance.
*/
public LFMultiThreadCachingTest(TestMethods testMethod) {
super(testMethod);
}
@Override
public void doTest() {
Map<String, Object> data = getTestMethod().getTestCaseData();
ConcurrentLinkedQueue<MethodHandle> adapters = new ConcurrentLinkedQueue<>();
CyclicBarrier begin = new CyclicBarrier(CORES);
CountDownLatch end = new CountDownLatch(CORES);
final Map<Thread, Throwable> threadUncaughtExceptions
= Collections.synchronizedMap(new HashMap<Thread, Throwable>(CORES));
for (int i = 0; i < CORES; ++i) {
TestMethods.Kind kind = KINDS[i % KINDS.length];
Thread t = new Thread(() -> {
try {
begin.await();
adapters.add(getTestMethod().getTestCaseMH(data, kind));
} catch (Throwable ex) {
threadUncaughtExceptions.put(Thread.currentThread(), ex);
} finally {
end.countDown();
}
});
t.start();
}
try {
end.await();
boolean vmeThrown = false;
boolean nonVmeThrown = false;
Throwable vme = null;
for (Map.Entry<Thread,
Throwable> entry : threadUncaughtExceptions.entrySet()) {
Thread t = entry.getKey();
Throwable e = entry.getValue();
System.err.printf("%nA thread with name \"%s\" of %d threads"
+ " has thrown exception:%n", t.getName(), CORES);
e.printStackTrace();
if (CodeCacheOverflowProcessor.isThrowableCausedByVME(e)) {
vmeThrown = true;
vme = e;
} else {
nonVmeThrown = true;
}
if (nonVmeThrown) {
throw new Error("One ore more threads have"
+ " thrown unexpected exceptions. See log.");
}
if (vmeThrown) {
throw new Error("One ore more threads have"
+ " thrown VirtualMachineError caused by"
+ " code cache overflow. See log.", vme);
}
}
} catch (InterruptedException ex) {
throw new Error("Unexpected exception: ", ex);
}
if (adapters.size() < CORES) {
throw new Error("adapters size[" + adapters.size() + "] is less than " + CORES);
}
MethodHandle prev = adapters.poll();
for (MethodHandle current : adapters) {
checkLFCaching(prev, current);
prev = current;
}
}
/**
* Main routine for multiple threaded lambda forms caching test.
*
* @param args Accepts no arguments.
*/
public static void main(String[] args) {
LambdaFormTestCase.runTests(LFMultiThreadCachingTest::new,
EnumSet.allOf(TestMethods.class));
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014, 2017, 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 LFSingleThreadCachingTest
* @bug 8046703
* @key randomness
* @summary Test verifies that lambda forms are cached when run with single thread
* @author kshefov
* @library /lib/testlibrary /java/lang/invoke/common /test/lib
* @modules java.base/java.lang.ref:open
* java.base/java.lang.invoke:open
* java.management
* @build jdk.test.lib.TimeLimitedRunner
* @build TestMethods
* @build LambdaFormTestCase
* @build LFCachingTestCase
* @build LFSingleThreadCachingTest
* @run main/othervm -XX:ReservedCodeCacheSize=128m LFSingleThreadCachingTest
*/
import java.lang.invoke.MethodHandle;
import java.util.EnumSet;
import java.util.Map;
/**
* Single threaded lambda forms caching test class.
*/
public final class LFSingleThreadCachingTest extends LFCachingTestCase {
/**
* Constructor for a single threaded lambda forms caching test case.
*
* @param testMethod A method from {@code j.l.i.MethodHandles} class that
* returns a {@code j.l.i.MethodHandle} instance.
*/
public LFSingleThreadCachingTest(TestMethods testMethod) {
super(testMethod);
}
@Override
public void doTest() {
MethodHandle adapter1;
MethodHandle adapter2;
Map<String, Object> data = getTestMethod().getTestCaseData();
try {
adapter1 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.ONE);
adapter2 = getTestMethod().getTestCaseMH(data, TestMethods.Kind.TWO);
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new Error("Unexpected exception", ex);
}
checkLFCaching(adapter1, adapter2);
}
/**
* Main routine for single threaded lambda forms caching test.
*
* @param args Accepts no arguments.
*/
public static void main(String[] args) {
LambdaFormTestCase.runTests(LFSingleThreadCachingTest::new,
EnumSet.allOf(TestMethods.class));
}
}

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import jdk.test.lib.TimeLimitedRunner;
import jdk.test.lib.Utils;
import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
import test.java.lang.invoke.lib.Helper;
import java.lang.invoke.MethodHandle;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
/**
* Lambda forms caching test case class. Contains all necessary test routines to
* test lambda forms caching in method handles returned by methods of
* MethodHandles class.
*
* @author kshefov
*/
public abstract class LambdaFormTestCase {
private static final long TIMEOUT = Helper.IS_THOROUGH ?
0L : (long) (Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT) * 0.2);
/**
* Reflection link to {@code j.l.i.MethodHandle.internalForm} method. It is
* used to get a lambda form from a method handle.
*/
protected static final Method INTERNAL_FORM;
protected static final Field KIND_FIELD;
protected static final Field REF_FIELD;
private static final List<GarbageCollectorMXBean> gcInfo;
private static long gcCount() {
return gcInfo.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum();
}
static {
try {
INTERNAL_FORM = MethodHandle.class.getDeclaredMethod("internalForm");
INTERNAL_FORM.setAccessible(true);
KIND_FIELD = Class.forName("java.lang.invoke.LambdaForm").getDeclaredField("kind");
KIND_FIELD.setAccessible(true);
REF_FIELD = Reference.class.getDeclaredField("referent");
REF_FIELD.setAccessible(true);
} catch (Exception ex) {
throw new Error("Unexpected exception", ex);
}
gcInfo = ManagementFactory.getGarbageCollectorMXBeans();
if (gcInfo.size() == 0) {
throw new Error("No GarbageCollectorMXBeans found.");
}
}
private final TestMethods testMethod;
private long gcCountAtStart;
private static class TestRun {
final Function<TestMethods, LambdaFormTestCase> ctor;
final Collection<TestMethods> testMethods;
final long totalIterations;
long doneIterations;
long testCounter;
long failCounter;
boolean passed;
TestRun(Function<TestMethods, LambdaFormTestCase> ctor,
Collection<TestMethods> testMethods) {
this.ctor = ctor;
this.testMethods = testMethods;
long testCaseNum = testMethods.size();
long iterations = Math.max(1, Helper.TEST_LIMIT / testCaseNum);
System.out.printf("Number of iterations according to -DtestLimit is %d (%d cases)%n",
iterations, iterations * testCaseNum);
System.out.printf("Number of iterations is set to %d (%d cases)%n",
iterations, iterations * testCaseNum);
System.out.flush();
totalIterations = iterations;
doneIterations = 0L;
testCounter = 0L;
failCounter = 0L;
passed = true;
}
Boolean doIteration() {
if (doneIterations >= totalIterations) {
return false;
}
System.err.println(String.format("Iteration %d:", doneIterations));
for (TestMethods testMethod : testMethods) {
LambdaFormTestCase testCase = ctor.apply(testMethod);
try {
System.err.printf("Tested LF caching feature"
+ " with MethodHandles.%s method.%n",
testCase.getTestMethod().name);
Throwable t = CodeCacheOverflowProcessor
.runMHTest(testCase::doTest);
if (t != null) {
return false;
}
System.err.println("PASSED");
} catch (OutOfMemoryError oome) {
// Don't swallow OOME so a heap dump can be created.
System.err.println("FAILED");
throw oome;
} catch (Throwable t) {
t.printStackTrace();
System.err.printf("FAILED. Caused by %s%n", t.getMessage());
passed = false;
failCounter++;
}
testCounter++;
}
doneIterations++;
return true;
}
}
/**
* Test case constructor. Generates test cases with random method types for
* given methods form {@code j.l.i.MethodHandles} class.
*
* @param testMethod A method from {@code j.l.i.MethodHandles} class which
* returns a {@code j.l.i.MethodHandle}.
*/
protected LambdaFormTestCase(TestMethods testMethod) {
this.testMethod = testMethod;
this.gcCountAtStart = gcCount();
}
public TestMethods getTestMethod() {
return testMethod;
}
protected boolean noGCHappened() {
return gcCount() == gcCountAtStart;
}
/**
* Routine that executes a test case.
*/
public abstract void doTest();
/**
* Runs a number of test cases defined by the size of testCases list.
*
* @param ctor constructor of LambdaFormCachingTest or its child classes
* object.
* @param testMethods list of test methods
*/
public static void runTests(Function<TestMethods, LambdaFormTestCase> ctor,
Collection<TestMethods> testMethods) {
LambdaFormTestCase.TestRun run
= new LambdaFormTestCase.TestRun(ctor, testMethods);
TimeLimitedRunner runner
= new TimeLimitedRunner(TIMEOUT, 4.0d, run::doIteration);
try {
runner.call();
} catch (Exception ex) {
System.err.println("FAILED");
throw new Error("Unexpected error!", ex);
}
if (!run.passed) {
throw new Error(String.format("%d of %d test cases FAILED! %n"
+ "Rerun the test with the same \"-Dseed=\" option as in the log file!",
run.failCounter, run.testCounter));
} else {
System.err.printf("All %d test cases PASSED!%n", run.testCounter);
}
}
}

View file

@ -0,0 +1,701 @@
/*
* Copyright (c) 2014, 2017, 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 test.java.lang.invoke.lib.Helper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Enumeration containing information about methods from
* {@code j.l.i.MethodHandles} class that are used for testing lambda forms
* caching.
*
* @author kshefov
*/
public enum TestMethods {
FOLD_ARGUMENTS("foldArguments") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
data.put("modifierMHArgNum", modifierMHArgNum);
Class<?> combinerReturnType;
if (realArity == 0) {
combinerReturnType = void.class;
} else {
combinerReturnType = Helper.RNG.nextBoolean() ?
void.class : mtTarget.parameterType(0);
}
data.put("combinerReturnType", combinerReturnType);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> combinerReturnType = (Class) data.get("combinerReturnType");
int modifierMHArgNum = (int) data.get("modifierMHArgNum");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
Class<?> rType = mtTarget.returnType();
int combListStart = (combinerReturnType == void.class) ? 0 : 1;
if (modifierMHArgNum < combListStart) {
modifierMHArgNum = combListStart;
}
MethodHandle combiner = TestMethods.methodHandleGenerator(combinerReturnType,
mtTarget.parameterList().subList(combListStart,
modifierMHArgNum), kind);
return MethodHandles.foldArguments(target, combiner);
}
},
DROP_ARGUMENTS("dropArguments") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int dropArgsPos = Helper.RNG.nextInt(realArity + 1);
data.put("dropArgsPos", dropArgsPos);
MethodType mtDropArgs = TestMethods.randomMethodTypeGenerator(
Helper.RNG.nextInt(super.maxArity - realArity));
data.put("mtDropArgs", mtDropArgs);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtDropArgs = (MethodType) data.get("mtDropArgs");
int dropArgsPos = (int) data.get("dropArgsPos");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
int mtTgtSlotsCount = TestMethods.argSlotsCount(mtTarget);
int mtDASlotsCount = TestMethods.argSlotsCount(mtDropArgs);
List<Class<?>> fakeParList;
if (mtTgtSlotsCount + mtDASlotsCount > super.maxArity - 1) {
fakeParList = TestMethods.reduceArgListToSlotsCount(mtDropArgs.parameterList(),
super.maxArity - mtTgtSlotsCount - 1);
} else {
fakeParList = mtDropArgs.parameterList();
}
return MethodHandles.dropArguments(target, dropArgsPos, fakeParList);
}
},
EXPLICIT_CAST_ARGUMENTS("explicitCastArguments", Helper.MAX_ARITY / 2) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
MethodType mtExcplCastArgs = TestMethods.randomMethodTypeGenerator(realArity);
if (mtTarget.returnType() == void.class) {
mtExcplCastArgs = MethodType.methodType(void.class,
mtExcplCastArgs.parameterArray());
}
if (mtExcplCastArgs.returnType() == void.class) {
mtExcplCastArgs = MethodType.methodType(mtTarget.returnType(),
mtExcplCastArgs.parameterArray());
}
data.put("mtExcplCastArgs", mtExcplCastArgs);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtExcplCastArgs = (MethodType) data.get("mtExcplCastArgs");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
return MethodHandles.explicitCastArguments(target, mtExcplCastArgs);
}
},
FILTER_ARGUMENTS("filterArguments", Helper.MAX_ARITY / 2) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int filterArgsPos = Helper.RNG.nextInt(realArity + 1);
data.put("filterArgsPos", filterArgsPos);
int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos);
data.put("filtersArgsArrayLength", filtersArgsArrayLength);
MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength);
data.put("mtFilter", mtFilter);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtFilter = (MethodType) data.get("mtFilter");
int filterArgsPos = (int) data.get("filterArgsPos");
int filtersArgsArrayLength = (int) data.get("filtersArgsArrayLength");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
MethodHandle[] filters = new MethodHandle[filtersArgsArrayLength];
for (int i = 0; i < filtersArgsArrayLength; i++) {
filters[i] = TestMethods.filterGenerator(mtFilter.parameterType(i),
mtTarget.parameterType(filterArgsPos + i), kind);
}
return MethodHandles.filterArguments(target, filterArgsPos, filters);
}
},
FILTER_RETURN_VALUE("filterReturnValue") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int filterArgsPos = Helper.RNG.nextInt(realArity + 1);
int filtersArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - filterArgsPos);
MethodType mtFilter = TestMethods.randomMethodTypeGenerator(filtersArgsArrayLength);
data.put("mtFilter", mtFilter);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtFilter = (MethodType) data.get("mtFilter");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
MethodHandle filter = TestMethods.filterGenerator(mtTarget.returnType(),
mtFilter.returnType(), kind);
return MethodHandles.filterReturnValue(target, filter);
}
},
INSERT_ARGUMENTS("insertArguments", Helper.MAX_ARITY - 3) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int insertArgsPos = Helper.RNG.nextInt(realArity + 1);
data.put("insertArgsPos", insertArgsPos);
int insertArgsArrayLength = Helper.RNG.nextInt(realArity + 1 - insertArgsPos);
MethodType mtInsertArgs = MethodType.methodType(void.class, mtTarget.parameterList()
.subList(insertArgsPos, insertArgsPos + insertArgsArrayLength));
data.put("mtInsertArgs", mtInsertArgs);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtInsertArgs = (MethodType) data.get("mtInsertArgs");
int insertArgsPos = (int) data.get("insertArgsPos");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
Object[] insertList = Helper.randomArgs(mtInsertArgs.parameterList());
return MethodHandles.insertArguments(target, insertArgsPos, insertList);
}
},
PERMUTE_ARGUMENTS("permuteArguments", Helper.MAX_ARITY / 2) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int[] permuteArgsReorderArray = new int[realArity];
int mtPermuteArgsNum = Helper.RNG.nextInt(Helper.MAX_ARITY);
mtPermuteArgsNum = mtPermuteArgsNum == 0 ? 1 : mtPermuteArgsNum;
MethodType mtPermuteArgs = TestMethods.randomMethodTypeGenerator(mtPermuteArgsNum);
mtTarget = mtTarget.changeReturnType(mtPermuteArgs.returnType());
for (int i = 0; i < realArity; i++) {
int mtPermuteArgsParNum = Helper.RNG.nextInt(mtPermuteArgs.parameterCount());
permuteArgsReorderArray[i] = mtPermuteArgsParNum;
mtTarget = mtTarget.changeParameterType(
i, mtPermuteArgs.parameterType(mtPermuteArgsParNum));
}
data.put("mtTarget", mtTarget);
data.put("permuteArgsReorderArray", permuteArgsReorderArray);
data.put("mtPermuteArgs", mtPermuteArgs);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
MethodType mtPermuteArgs = (MethodType) data.get("mtPermuteArgs");
int[] permuteArgsReorderArray = (int[]) data.get("permuteArgsReorderArray");
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), kind);
return MethodHandles.permuteArguments(target, mtPermuteArgs, permuteArgsReorderArray);
}
},
THROW_EXCEPTION("throwException") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> rType = mtTarget.returnType();
return MethodHandles.throwException(rType, Exception.class
);
}
},
GUARD_WITH_TEST("guardWithTest") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
data.put("modifierMHArgNum", modifierMHArgNum);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
int modifierMHArgNum = (int) data.get("modifierMHArgNum");
TestMethods.Kind targetKind;
TestMethods.Kind fallbackKind;
if (kind.equals(TestMethods.Kind.ONE)) {
targetKind = TestMethods.Kind.ONE;
fallbackKind = TestMethods.Kind.TWO;
} else {
targetKind = TestMethods.Kind.TWO;
fallbackKind = TestMethods.Kind.ONE;
}
MethodHandle target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), targetKind);
MethodHandle fallback = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), fallbackKind);
MethodHandle test = TestMethods.methodHandleGenerator(boolean.class,
mtTarget.parameterList().subList(0, modifierMHArgNum), kind);
return MethodHandles.guardWithTest(test, target, fallback);
}
},
CATCH_EXCEPTION("catchException") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
data.put("modifierMHArgNum", modifierMHArgNum);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodType mtTarget = (MethodType) data.get("mtTarget");
int modifierMHArgNum = (int) data.get("modifierMHArgNum");
MethodHandle target;
if (kind.equals(TestMethods.Kind.ONE)) {
target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), TestMethods.Kind.ONE);
} else {
target = TestMethods.methodHandleGenerator(mtTarget.returnType(),
mtTarget.parameterList(), TestMethods.Kind.EXCEPT);
}
List<Class<?>> handlerParamList = new ArrayList<>(mtTarget.parameterCount() + 1);
handlerParamList.add(Exception.class);
handlerParamList.addAll(mtTarget.parameterList().subList(0, modifierMHArgNum));
MethodHandle handler = TestMethods.methodHandleGenerator(
mtTarget.returnType(), handlerParamList, TestMethods.Kind.TWO);
return MethodHandles.catchException(target, Exception.class, handler);
}
},
INVOKER("invoker", Helper.MAX_ARITY - 1) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
return MethodHandles.invoker(mtTarget);
}
},
EXACT_INVOKER("exactInvoker", Helper.MAX_ARITY - 1) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
return MethodHandles.exactInvoker(mtTarget);
}
},
SPREAD_INVOKER("spreadInvoker", Helper.MAX_ARITY - 1) {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
// Arity after reducing because of long and double take 2 slots.
int realArity = mtTarget.parameterCount();
int modifierMHArgNum = Helper.RNG.nextInt(realArity + 1);
data.put("modifierMHArgNum", modifierMHArgNum);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
int modifierMHArgNum = (int) data.get("modifierMHArgNum");
return MethodHandles.spreadInvoker(mtTarget, modifierMHArgNum);
}
},
ARRAY_ELEMENT_GETTER("arrayElementGetter") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> rType = mtTarget.returnType();
if (rType == void.class) {
rType = Object.class;
}
return MethodHandles.arrayElementGetter(Array.newInstance(rType, 2).getClass());
}
},
ARRAY_ELEMENT_SETTER("arrayElementSetter") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> rType = mtTarget.returnType();
if (rType == void.class) {
rType = Object.class;
}
return MethodHandles.arrayElementSetter(Array.newInstance(rType, 2).getClass());
}
},
CONSTANT("constant") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> rType = mtTarget.returnType();
if (rType == void.class) {
rType = Object.class;
}
if (rType.equals(boolean.class)) {
// There should be the same return values because for default values there are special "zero" forms
return MethodHandles.constant(rType, true);
} else {
return MethodHandles.constant(rType, kind.getValue(rType));
}
}
},
IDENTITY("identity") {
@Override
public Map<String, Object> getTestCaseData() {
Map<String, Object> data = new HashMap<>();
int desiredArity = Helper.RNG.nextInt(super.maxArity);
MethodType mtTarget = TestMethods.randomMethodTypeGenerator(desiredArity);
data.put("mtTarget", mtTarget);
return data;
}
@Override
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind) {
MethodType mtTarget = (MethodType) data.get("mtTarget");
Class<?> rType = mtTarget.returnType();
if (rType == void.class) {
rType = Object.class;
}
return MethodHandles.identity(rType);
}
};
/**
* Test method's name.
*/
public final String name;
private final int maxArity;
private TestMethods(String name, int maxArity) {
this.name = name;
this.maxArity = maxArity;
}
private TestMethods(String name) {
this(name, Helper.MAX_ARITY);
}
protected MethodHandle getMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
throw new UnsupportedOperationException(
"TESTBUG: getMH method is not implemented for test method " + this);
}
/**
* Creates an adapter method handle depending on a test method from
* MethodHandles class. Adapter is what is returned by the test method. This
* method is able to create two kinds of adapters, their type will be the
* same, but return values are different.
*
* @param data a Map containing data to create a method handle, can be
* obtained by {@link #getTestCaseData} method
* @param kind defines whether adapter ONE or adapter TWO will be
* initialized. Should be equal to TestMethods.Kind.ONE or
* TestMethods.Kind.TWO
* @return Method handle adapter that behaves according to
* TestMethods.Kind.ONE or TestMethods.Kind.TWO
* @throws java.lang.NoSuchMethodException
* @throws java.lang.IllegalAccessException
*/
public MethodHandle getTestCaseMH(Map<String, Object> data, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
if (data == null) {
throw new Error(String.format("TESTBUG: Data for test method %s is not prepared",
this.name));
}
if (!kind.equals(TestMethods.Kind.ONE) && !kind.equals(TestMethods.Kind.TWO)) {
throw new IllegalArgumentException("TESTBUG: Wrong \"kind\" (" + kind
+ ") arg to getTestCaseMH function."
+ " Should be Kind.ONE or Kind.TWO");
}
return getMH(data, kind);
}
/**
* Returns a data Map needed for {@link #getTestCaseMH} method.
*
* @return data Map needed for {@link #getTestCaseMH} method
*/
public Map<String, Object> getTestCaseData() {
throw new UnsupportedOperationException(
"TESTBUG: getTestCaseData method is not implemented for test method " + this);
}
/**
* Enumeration used in methodHandleGenerator to define whether a MH returned
* by this method returns "2" in different type representations, "4", or
* throw an Exception.
*/
public static enum Kind {
ONE(2),
TWO(4),
EXCEPT(0);
private final int value;
private Object getValue(Class<?> cl) {
return Helper.castToWrapper(value, cl);
}
private MethodHandle getBasicMH(Class<?> rType)
throws NoSuchMethodException, IllegalAccessException {
MethodHandle result = null;
switch (this) {
case ONE:
case TWO:
if (rType.equals(void.class)) {
result = MethodHandles.lookup().findVirtual(Kind.class,
"returnVoid", MethodType.methodType(void.class));
result = MethodHandles.insertArguments(result, 0, this);
} else {
result = MethodHandles.constant(rType, getValue(rType));
}
break;
case EXCEPT:
result = MethodHandles.throwException(rType, Exception.class);
result = MethodHandles.insertArguments(result, 0, new Exception());
break;
}
return result;
}
private void returnVoid() {
}
private Kind(int value) {
this.value = value;
}
}
/**
* Routine used to obtain a randomly generated method type.
*
* @param arity Arity of returned method type.
* @return MethodType generated randomly.
*/
private static MethodType randomMethodTypeGenerator(int arity) {
return Helper.randomMethodTypeGenerator(arity);
}
/**
* Routine used to obtain a method handles of a given type an kind (return
* value).
*
* @param returnType Type of MH return value.
* @param argTypes Types of MH args.
* @param kind Defines whether the obtained MH returns "1" or "2".
* @return Method handle of the given type.
* @throws NoSuchMethodException
* @throws IllegalAccessException
*/
private static MethodHandle methodHandleGenerator(Class<?> returnType,
List<Class<?>> argTypes, TestMethods.Kind kind)
throws NoSuchMethodException, IllegalAccessException {
MethodHandle result;
result = kind.getBasicMH(returnType);
return Helper.addTrailingArgs(result, argTypes.size(), argTypes);
}
/**
* Routine that generates filter method handles to test
* MethodHandles.filterArguments method.
*
* @param inputType Filter's argument type.
* @param returnType Filter's return type.
* @param kind Filter's return value definer.
* @return A filter method handle, that takes one argument.
* @throws NoSuchMethodException
* @throws IllegalAccessException
*/
private static MethodHandle filterGenerator(Class<?> inputType, Class<?> returnType,
TestMethods.Kind kind) throws NoSuchMethodException, IllegalAccessException {
MethodHandle tmpMH = kind.getBasicMH(returnType);
if (inputType.equals(void.class)) {
return tmpMH;
}
ArrayList<Class<?>> inputTypeList = new ArrayList<>(1);
inputTypeList.add(inputType);
return Helper.addTrailingArgs(tmpMH, 1, inputTypeList);
}
private static int argSlotsCount(MethodType mt) {
int result = 0;
for (Class cl : mt.parameterArray()) {
if (cl.equals(long.class) || cl.equals(double.class)) {
result += 2;
} else {
result++;
}
}
return result;
}
private static List<Class<?>> reduceArgListToSlotsCount(List<Class<?>> list,
int desiredSlotCount) {
List<Class<?>> result = new ArrayList<>(desiredSlotCount);
int count = 0;
for (Class<?> cl : list) {
if (count >= desiredSlotCount) {
break;
}
if (cl.equals(long.class) || cl.equals(double.class)) {
count += 2;
} else {
count++;
}
result.add(cl);
}
return result;
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 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.
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
/*
* @test
* @bug 8297727
* @summary MethodHandle field access fails with bytecode compilation disabled
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames
* -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=-1
* ReflectionInInterpretTest
*/
public class ReflectionInInterpretTest {
private static Integer f; // non-Object type is required to find a non-compiled form
public static void main(String... args) throws Throwable {
MethodHandle mh = MethodHandles.lookup().findStaticGetter(ReflectionInInterpretTest.class, "f", Integer.class);
var _ = (Integer) mh.invokeExact();
}
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2014, 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 unit tests for java.lang.invoke.LambdaForm
* @modules java.base/java.lang.invoke:open
* @run junit/othervm test.java.lang.invoke.LambdaFormTest
*/
package test.java.lang.invoke;
import org.junit.Test;
import java.lang.reflect.Method;
import static org.junit.Assert.*;
public class LambdaFormTest {
static final Method M_shortenSignature;
static {
try {
Class<?> impl = Class.forName("java.lang.invoke.LambdaForm", false, null);
Method m = impl.getDeclaredMethod("shortenSignature", String.class);
m.setAccessible(true);
M_shortenSignature = m;
} catch(Exception e) {
throw new AssertionError(e);
}
}
public static String shortenSignature(String signature) throws ReflectiveOperationException {
return (String)M_shortenSignature.invoke(null, signature);
}
@Test
public void testShortenSignature() throws ReflectiveOperationException {
for (String s : new String[] {
// invariant strings:
"L", "LL", "ILL", "LIL", "LLI", "IILL", "ILIL", "ILLI",
// a few mappings:
"LLL=L3", "LLLL=L4", "LLLLLLLLLL=L10",
"IIIDDD=I3D3", "IDDD=ID3", "IIDDD=IID3", "IIID=I3D", "IIIDD=I3DD"
}) {
String s2 = s.substring(s.indexOf('=')+1);
String s1 = s.equals(s2) ? s : s.substring(0, s.length() - s2.length() - 1);
// mix the above cases with before and after reps of Z*
for (int k = -3; k <= 3; k++) {
String beg = (k < 0 ? "ZZZZ".substring(-k) : "");
String end = (k > 0 ? "ZZZZ".substring(+k) : "");
String ks1 = beg+s1+end;
String ks2 = shortenSignature(beg)+s2+shortenSignature(end);
String ks3 = shortenSignature(ks1);
assertEquals(ks2, ks3);
}
}
}
public static void main(String[] args) throws ReflectiveOperationException {
LambdaFormTest test = new LambdaFormTest();
test.testShortenSignature();
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8160717
* @run main/othervm -ea -esa -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=-1 test.java.lang.invoke.LoopCombinatorLongSignatureTest
* @run main/othervm -ea -esa test.java.lang.invoke.LoopCombinatorLongSignatureTest
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
/**
* If a loop with an excessive amount of clauses is created, so that the number of parameters to the resulting loop
* handle exceeds the allowed maximum, an IAE must be signalled. The test is run first in LambdaForm interpretation mode
* and then in default mode, wherein bytecode generation falls back to LFI mode due to excessively long methods.
* <p>
* By default, the test run only checks whether loop handle construction succeeds and fails. If executing the generated
* loops is desired, this should be indicated by setting the {@code java.lang.invoke.LoopCombinatorLongSignatureTest.RUN}
* environment variable to {@code true}. This is disabled by default as it considerably increases the time needed to run
* the test.
*/
public class LoopCombinatorLongSignatureTest {
static final MethodHandle INIT = MethodHandles.constant(int.class, 0);
static final MethodHandle STEP = MethodHandles.identity(int.class);
static final MethodHandle PRED_F = MethodHandles.constant(boolean.class, false);
static final MethodHandle PRED_T = MethodHandles.constant(boolean.class, true);
static final MethodHandle FINI = MethodHandles.identity(int.class);
static final int ARG_LIMIT = 254; // for internal reasons, this is the maximum allowed number of arguments
public static void main(String[] args) {
boolean run = Boolean.parseBoolean(
System.getProperty("java.lang.invoke.LoopCombinatorLongSignatureTest.RUN", "false"));
for (int loopArgs = 0; loopArgs < 2; ++loopArgs) {
testLongSignature(loopArgs, false, run);
testLongSignature(loopArgs, true, run);
}
}
static void testLongSignature(int loopArgs, boolean excessive, boolean run) {
int nClauses = ARG_LIMIT - loopArgs + (excessive ? 1 : 0);
System.out.print((excessive ? "(EXCESSIVE)" : "(LONG )") + " arguments: " + loopArgs + ", clauses: " + nClauses + " -> ");
// extend init to denote what arguments the loop should accept
Class<?>[] argTypes = new Class<?>[loopArgs];
Arrays.fill(argTypes, int.class);
MethodHandle init = MethodHandles.dropArguments(INIT, 0, argTypes);
// build clauses
MethodHandle[][] clauses = new MethodHandle[nClauses][];
MethodHandle[] clause = {init, STEP, PRED_T, FINI};
MethodHandle[] fclause = {init, STEP, PRED_F, FINI};
Arrays.fill(clauses, clause);
clauses[nClauses - 1] = fclause; // make the last clause terminate the loop
try {
MethodHandle loop = MethodHandles.loop(clauses);
if (excessive) {
throw new AssertionError("loop construction should have failed");
} else if (run) {
int r;
if (loopArgs == 0) {
r = (int) loop.invoke();
} else {
Object[] args = new Object[loopArgs];
Arrays.fill(args, 0);
r = (int) loop.invokeWithArguments(args);
}
System.out.println("SUCCEEDED (OK) -> " + r);
} else {
System.out.println("SUCCEEDED (OK)");
}
} catch (IllegalArgumentException iae) {
if (excessive) {
System.out.println("FAILED (OK)");
} else {
iae.printStackTrace(System.out);
throw new AssertionError("loop construction should not have failed (see above)");
}
} catch (Throwable t) {
t.printStackTrace(System.out);
throw new AssertionError("unexpected failure (see above)");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2010, 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.
*/
/* @test
* @bug 8022066
* @summary smoke test for method handle constants
* @build indify.Indify
* @compile MethodHandleConstants.java
* @run main/othervm
* indify.Indify
* --verify-specifier-count=4
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.MethodHandleConstants --check-output
* @run main/othervm
* indify.Indify
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.MethodHandleConstants
*/
package test.java.lang.invoke;
import java.util.*;
import java.io.*;
import java.lang.invoke.*;
import java.security.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
public class MethodHandleConstants {
public static void main(String... av) throws Throwable {
if (av.length > 0 && av[0].equals("--check-output")) openBuf();
System.out.println("Obtaining method handle constants:");
testCase(MH_String_replace_C2(), String.class, "replace", String.class, String.class, char.class, char.class);
testCase(MH_MethodHandle_invokeExact_SC2(), MethodHandle.class, "invokeExact", String.class, MethodHandle.class, String.class, char.class, char.class);
testCase(MH_MethodHandle_invoke_SC2(), MethodHandle.class, "invoke", String.class, MethodHandle.class, String.class, char.class, char.class);
testCase(MH_Class_forName_S(), Class.class, "forName", Class.class, String.class);
testCase(MH_Class_forName_SbCL(), Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class);
System.out.println("Done.");
closeBuf();
}
private static void testCase(MethodHandle mh, Class<?> defc, String name, Class<?> rtype, Class<?>... ptypes) throws Throwable {
System.out.println(mh);
// we include defc, because we assume it is a non-static MH:
MethodType mt = methodType(rtype, ptypes);
assertEquals(mh.type(), mt);
// FIXME: Use revealDirect to find out more
}
private static void assertEquals(Object exp, Object act) {
if (exp == act || (exp != null && exp.equals(act))) return;
throw new AssertionError("not equal: "+exp+", "+act);
}
private static PrintStream oldOut;
private static ByteArrayOutputStream buf;
private static void openBuf() {
oldOut = System.out;
buf = new ByteArrayOutputStream();
System.setOut(new PrintStream(buf));
}
private static void closeBuf() {
if (buf == null) return;
System.out.flush();
System.setOut(oldOut);
String[] haveLines = new String(buf.toByteArray()).split("[\n\r]+");
for (String line : haveLines) System.out.println(line);
Iterator<String> iter = Arrays.asList(haveLines).iterator();
for (String want : EXPECT_OUTPUT) {
String have = iter.hasNext() ? iter.next() : "[EOF]";
if (want.equals(have)) continue;
System.err.println("want line: "+want);
System.err.println("have line: "+have);
throw new AssertionError("unexpected output: "+have);
}
if (iter.hasNext())
throw new AssertionError("unexpected output: "+iter.next());
}
private static final String[] EXPECT_OUTPUT = {
"Obtaining method handle constants:",
"MethodHandle(String,char,char)String",
"MethodHandle(MethodHandle,String,char,char)String",
"MethodHandle(MethodHandle,String,char,char)String",
"MethodHandle(String)Class",
"MethodHandle(String,boolean,ClassLoader)Class",
"Done."
};
// String.replace(String, char, char)
private static MethodType MT_String_replace_C2() {
shouldNotCallThis();
return methodType(String.class, char.class, char.class);
}
private static MethodHandle MH_String_replace_C2() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findVirtual(String.class, "replace", MT_String_replace_C2());
}
// MethodHandle.invokeExact(...)
private static MethodType MT_MethodHandle_invokeExact_SC2() {
shouldNotCallThis();
return methodType(String.class, String.class, char.class, char.class);
}
private static MethodHandle MH_MethodHandle_invokeExact_SC2() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findVirtual(MethodHandle.class, "invokeExact", MT_MethodHandle_invokeExact_SC2());
}
// MethodHandle.invoke(...)
private static MethodType MT_MethodHandle_invoke_SC2() {
shouldNotCallThis();
return methodType(String.class, String.class, char.class, char.class);
}
private static MethodHandle MH_MethodHandle_invoke_SC2() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findVirtual(MethodHandle.class, "invoke", MT_MethodHandle_invoke_SC2());
}
// Class.forName(String)
private static MethodType MT_Class_forName_S() {
shouldNotCallThis();
return methodType(Class.class, String.class);
}
private static MethodHandle MH_Class_forName_S() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(Class.class, "forName", MT_Class_forName_S());
}
// Class.forName(String, boolean, ClassLoader)
private static MethodType MT_Class_forName_SbCL() {
shouldNotCallThis();
return methodType(Class.class, String.class, boolean.class, ClassLoader.class);
}
private static MethodHandle MH_Class_forName_SbCL() throws ReflectiveOperationException {
shouldNotCallThis();
return lookup().findStatic(Class.class, "forName", MT_Class_forName_SbCL());
}
private static void shouldNotCallThis() {
// if this gets called, the transformation has not taken place
if (System.getProperty("MethodHandleConstants.allow-untransformed") != null) return;
throw new AssertionError("this code should be statically transformed away by Indify");
}
}

View file

@ -0,0 +1,72 @@
/*
* 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
* @summary Test MethodHandle::invokeExact and MethodHandle::invoke throws
* UnsupportedOperationException when called via Method::invoke
* @run junit test.java.lang.invoke.MethodHandleInvokeUOE
*/
package test.java.lang.invoke;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.invoke.MethodType.*;
import org.junit.jupiter.api.Test;
public class MethodHandleInvokeUOE {
@Test
public void testInvokeExact() throws Throwable {
Class<?> clz = MethodHandle.class;
String mname = "invokeExact";
Method m = clz.getDeclaredMethod(mname, Object[].class);
MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
try {
m.invoke(mh, new Object[1]);
} catch (InvocationTargetException e) {
if (!(e.getCause() instanceof UnsupportedOperationException)) {
throw new RuntimeException("expected UnsupportedOperationException but got: "
+ e.getCause().getClass().getName(), e);
}
}
}
@Test
public void testInvoke() throws Throwable {
Class<?> clz = MethodHandle.class;
String mname = "invoke";
Method m = clz.getDeclaredMethod(mname, Object[].class);
MethodHandle mh = MethodHandles.lookup().findVirtual(clz, mname, methodType(Object.class, Object[].class));
try {
m.invoke(mh, new Object[1]);
} catch (InvocationTargetException e) {
if (!(e.getCause() instanceof UnsupportedOperationException)) {
throw new RuntimeException("expected UnsupportedOperationException but got: "
+ e.getCause().getClass().getName(), e);
}
}
}
}

View file

@ -0,0 +1,403 @@
/*
* Copyright (c) 2023, 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.
*/
import java.lang.classfile.ClassHierarchyResolver;
import java.lang.classfile.ClassFile;
import org.junit.jupiter.api.Test;
import java.io.Closeable;
import java.io.IOException;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.WrongMethodTypeException;
import java.lang.reflect.AccessFlag;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.IntSupplier;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.MethodHandleProxies.*;
import static java.lang.invoke.MethodType.genericMethodType;
import static java.lang.invoke.MethodType.methodType;
import static java.lang.classfile.ClassFile.*;
import static org.junit.jupiter.api.Assertions.*;
/*
* @test
* @bug 6983726 8206955 8269351 8350549
* @summary Basic sanity tests for MethodHandleProxies
* @build BasicTest Client
* @run junit BasicTest
*/
public class BasicTest {
@Test
public void testUsual() throws Throwable {
AtomicInteger ai = new AtomicInteger(5);
var mh = MethodHandles.lookup().findVirtual(AtomicInteger.class, "getAndIncrement", methodType(int.class));
IntSupplier is = asInterfaceInstance(IntSupplier.class, mh.bindTo(ai));
assertEquals(5, is.getAsInt());
assertEquals(6, is.getAsInt());
assertEquals(7, is.getAsInt());
}
/**
* Established null behaviors of MHP API.
*/
@Test
public void testNulls() {
assertThrows(NullPointerException.class, () ->
asInterfaceInstance(null, MethodHandles.zero(void.class)),
"asInterfaceInstance - intfc");
assertThrows(NullPointerException.class, () ->
asInterfaceInstance(Runnable.class, null),
"asInterfaceInstance - target");
assertFalse(isWrapperInstance(null), "isWrapperInstance");
assertThrows(IllegalArgumentException.class, () -> wrapperInstanceTarget(null),
"wrapperInstanceTarget");
assertThrows(IllegalArgumentException.class, () -> wrapperInstanceType(null),
"wrapperInstanceType");
}
@Test
public void testWrapperInstance() throws Throwable {
var mh = MethodHandles.publicLookup()
.findVirtual(Integer.class, "compareTo", methodType(int.class, Integer.class));
@SuppressWarnings("unchecked")
Comparator<Integer> proxy = (Comparator<Integer>) asInterfaceInstance(Comparator.class, mh);
assertTrue(isWrapperInstance(proxy));
assertSame(mh, wrapperInstanceTarget(proxy));
assertSame(Comparator.class, wrapperInstanceType(proxy));
}
/**
* Tests undeclared exceptions and declared exceptions in proxies.
*/
@Test
public void testThrowables() {
// don't wrap
assertThrows(Error.class, throwing(Error.class, new Error())::close,
"Errors should be propagated");
assertThrows(RuntimeException.class, throwing(RuntimeException.class, new RuntimeException())::close,
"RuntimeException should be propagated");
assertThrows(IOException.class, throwing(IOException.class, new IOException())::close,
"Declared IOException should be propagated");
// wrap
assertThrows(UndeclaredThrowableException.class, throwing(IllegalAccessException.class,
new IllegalAccessException())::close,
"Undeclared IllegalAccessException should be wrapped");
}
/**
* Tests that invalid interfaces are rejected.
*/
@Test
public void testRejects() {
var mh = MethodHandles.constant(String.class, "42");
assertThrows(IllegalArgumentException.class, () -> asInterfaceInstance(PackagePrivate.class, mh),
"non-public interface");
assertThrows(IllegalArgumentException.class, () -> asInterfaceInstance(loadHidden(), mh),
"hidden interface");
assertThrows(IllegalArgumentException.class, () -> asInterfaceInstance(MultiAbstractMethods.class, mh),
"multiple abstract method names");
assertThrows(IllegalArgumentException.class, () -> asInterfaceInstance(NoAbstractMethods.class, mh),
"no abstract method");
assertThrows(IllegalArgumentException.class, () -> asInterfaceInstance(Sealed.class, mh),
"sealed interface");
}
/**
* Tests that non-sealed interfaces can be implemented.
*/
@Test
public void testNonSealed() {
MethodHandle target = MethodHandles.constant(String.class, "Non-Sealed");
NonSealed proxy = asInterfaceInstance(NonSealed.class, target);
assertEquals(proxy.m(), "Non-Sealed");
}
/**
* Tests that Proxy correctly proxies potential bridge abstract methods.
*/
@Test
public void testMultiSameName() throws Throwable {
var baseAndChild = loadBaseAndChild();
var baseClass = baseAndChild.get(0);
var childClass = baseAndChild.get(1);
checkMethods(childClass.getMethods());
checkMethods(childClass.getDeclaredMethods());
var lookup = MethodHandles.lookup();
var baseValueMh = lookup.findVirtual(baseClass, "value", genericMethodType(0))
.asType(genericMethodType(1));
var childIntegerValueMh = lookup.findVirtual(childClass, "value", methodType(Integer.class))
.asType(methodType(Integer.class, Object.class));
var childIntValueMh = lookup.findVirtual(childClass, "value", methodType(int.class))
.asType(methodType(int.class, Object.class));
Object child = asInterfaceInstance(childClass, MethodHandles.constant(Integer.class, 7));
assertEquals(7, (Object) baseValueMh.invokeExact(child));
assertEquals(7, (Integer) childIntegerValueMh.invokeExact(child));
assertEquals(7, (int) childIntValueMh.invokeExact(child));
}
/**
* Tests that default methods can be used.
*/
@Test
public void testDefaultMethods() {
MethodHandle target = MethodHandles.constant(String.class, "F");
C proxy = asInterfaceInstance(C.class, target);
assertEquals(proxy.f(), "F");
assertEquals(proxy.a(), "A");
assertEquals(proxy.b(), "B");
assertEquals(proxy.c(), "C");
assertEquals(proxy.concat(), "ABC");
}
/**
* Tests that correct implementation of default methods are called,
* and correct abstract methods are implemented.
*/
@Test
public void testOverrides() {
MethodHandle target = MethodHandles.constant(String.class, "concat");
D proxy = asInterfaceInstance(D.class, target);
assertEquals(proxy.a(), "OA");
assertEquals(proxy.b(), "OB");
assertEquals(proxy.c(), "OC");
assertEquals(proxy.f(), "OF");
assertEquals(proxy.concat(), "concat");
}
/**
* Tests primitive type conversions in proxies.
*/
@Test
public void testPrimitiveConversion() throws Throwable {
var mh = MethodHandles.lookup().findStatic(BasicTest.class, "mul",
methodType(long.class, int.class));
@SuppressWarnings("unchecked")
Function<Integer, Long> func = (Function<Integer, Long>) asInterfaceInstance(Function.class, mh);
assertEquals(32423432L * 32423432L, func.apply(32423432));
@SuppressWarnings("unchecked")
ToLongFunction<Integer> func1 = (ToLongFunction<Integer>) asInterfaceInstance(ToLongFunction.class, mh);
assertEquals(32423432L * 32423432L, func1.applyAsLong(32423432));
@SuppressWarnings("unchecked")
IntFunction<Long> func2 = (IntFunction<Long>) asInterfaceInstance(IntFunction.class, mh);
assertEquals(32423432L * 32423432L, func2.apply(32423432));
}
/**
* Tests common type conversions in proxies.
*/
@Test
public void testBasicConversion() {
var mh = MethodHandles.constant(String.class, "42");
asInterfaceInstance(Client.class, mh).exec(); // return value dropped, runs fine
var nullMh = MethodHandles.zero(String.class);
var badIterable = asInterfaceInstance(Iterable.class, nullMh);
assertNull(badIterable.iterator()); // null is convertible
}
/**
* Tests incompatible type conversions in proxy construction.
*/
@Test
public void testWrongConversion() {
var mh = MethodHandles.constant(String.class, "42");
assertThrows(WrongMethodTypeException.class, () -> asInterfaceInstance(IntSupplier.class, mh),
"cannot convert String return to int under any circumstance");
var proxy = asInterfaceInstance(Iterable.class, mh);
assertThrows(ClassCastException.class, proxy::iterator);
}
/**
* Verifies {@code isWrapperInstance} works under race and is thread safe
* like {@code Class} objects are.
*/
@Test
public void testRacyWrapperCheck() {
MethodHandle noop = MethodHandles.zero(void.class);
var lookup = MethodHandles.lookup();
AtomicInteger counter = new AtomicInteger();
Stream.generate(() -> {
String name = "MHPRaceIface" + counter.getAndIncrement();
var bytes = ClassFile.of().build(ClassDesc.of(name), clb ->
clb.withFlags(ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE)
.withMethod("sam", MTD_void, ACC_PUBLIC | ACC_ABSTRACT, _ -> {}));
try {
return lookup.defineClass(bytes);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}).parallel()
.map(cl -> MethodHandleProxies.asInterfaceInstance(cl, noop))
.limit(100)
.forEach(inst -> assertTrue(MethodHandleProxies.isWrapperInstance(inst),
() -> Objects.toIdentityString(inst) + " should pass wrapper test"));
}
private static <T extends Throwable> Closeable throwing(Class<T> clz, T value) {
return asInterfaceInstance(Closeable.class, MethodHandles.throwException(void.class, clz).bindTo(value));
}
private static long mul(int i) {
return (long) i * i;
}
void checkMethods(Method[] methods) {
assertTrue(methods.length > 1, () -> "Should have more than 1 declared methods, found only " + Arrays.toString(methods));
for (Method method : methods) {
assertTrue(method.accessFlags().contains(AccessFlag.ABSTRACT), () -> method + " is not abstract");
}
}
private Class<?> loadHidden() {
try (var is = BasicTest.class.getResourceAsStream("Client.class")) {
var bytes = Objects.requireNonNull(is).readAllBytes();
var lookup = MethodHandles.lookup();
return lookup.defineHiddenClass(bytes, true).lookupClass();
} catch (Throwable ex) {
return fail("Hidden interface loading failure", ex);
}
}
// Base: Object value();
// Child: Integer value(); int value();
private List<Class<?>> loadBaseAndChild() throws IllegalAccessException {
ClassDesc baseCd = ClassDesc.of("BasicTest$Base");
ClassDesc childCd = ClassDesc.of("BasicTest$Child");
var objMtd = MethodTypeDesc.of(CD_Object);
var integerMtd = MethodTypeDesc.of(CD_Integer);
var intMtd = MethodTypeDesc.of(CD_int);
var classfile = ClassFile.of(ClassFile.ClassHierarchyResolverOption.of(ClassHierarchyResolver.defaultResolver().orElse(
ClassHierarchyResolver.of(List.of(baseCd, childCd), Map.ofEntries(Map.entry(baseCd, CD_Object),
Map.entry(childCd, CD_Object))))));
var baseBytes = classfile.build(baseCd, clb -> {
clb.withSuperclass(CD_Object);
clb.withFlags(ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT);
clb.withMethod("value", objMtd, ACC_PUBLIC | ACC_ABSTRACT, mb -> {});
});
var lookup = MethodHandles.lookup();
var base = lookup.ensureInitialized(lookup.defineClass(baseBytes));
var childBytes = classfile.build(childCd, clb -> {
clb.withSuperclass(CD_Object);
clb.withInterfaceSymbols(baseCd);
clb.withFlags(ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT);
clb.withMethod("value", integerMtd, ACC_PUBLIC | ACC_ABSTRACT, mb -> {});
clb.withMethod("value", intMtd, ACC_PUBLIC | ACC_ABSTRACT, mb -> {});
});
var child = lookup.ensureInitialized(lookup.defineClass(childBytes));
return List.of(base, child);
}
public interface MultiAbstractMethods {
String a();
String b();
}
public interface NoAbstractMethods {
String toString();
}
interface PackagePrivate {
Object value();
}
public interface A {
default String a() {
return "A";
}
}
public interface B {
default String b() {
return "B";
}
}
public interface C extends A, B {
String f();
default String c() {
return "C";
}
default String concat() {
return a() + b() + c();
}
}
public interface D extends C {
String concat();
default String f() {
return "OF";
}
default String a() {
return "OA";
}
default String b() {
return "OB";
}
default String c() {
return "OC";
}
}
public sealed interface Sealed permits NonSealed {
String m();
}
public non-sealed interface NonSealed extends Sealed {
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2023, 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;
/**
* A general-purposed public client interface for testing.
*/
public interface Client {
/**
* Returns a lookup from Client.class.
*/
static Lookup lookup() {
return MethodHandles.lookup();
}
void exec();
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2022, 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 8280377
* @build m1/* m2/* Unnamed
* @run junit/othervm m1/p1.Main
* @run junit/othervm Unnamed
* @summary Test MethodHandleProxies::asInterfaceInstance with a default
* method with varargs
*/

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022, 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.
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*
* Test MethodHandleProxies::asInterfaceInstance with an inaccessible interface
*/
public class Unnamed {
@Test
void testInaccessible() throws Throwable {
MethodHandle target = MethodHandles.constant(String.class, "test");
Class<?> intf = Class.forName("p2.TestIntf");
Object t = MethodHandleProxies.asInterfaceInstance(intf, target);
// verify that the caller has no access to the proxy created on an
// inaccessible interface
Method m = intf.getMethod("test");
assertFalse(m.canAccess(t));
}
}

View file

@ -0,0 +1,211 @@
/*
* Copyright (c) 2023, 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.lang.classfile.ClassFile;
import jdk.test.lib.util.ForceGC;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.ref.WeakReference;
import java.util.Comparator;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.MethodHandleProxies.*;
import static java.lang.invoke.MethodType.methodType;
import static java.lang.classfile.ClassFile.*;
import static org.junit.jupiter.api.Assertions.*;
/*
* @test
* @bug 6983726
* @library /test/lib
* @summary Tests on implementation hidden classes spinned by MethodHandleProxies
* @build WrapperHiddenClassTest Client jdk.test.lib.util.ForceGC
* @run junit WrapperHiddenClassTest
*/
public class WrapperHiddenClassTest {
/**
* Tests an adversary "implementation" class will not be
* "recovered" by the wrapperInstance* APIs
*/
@Test
public void testWrapperInstance() throws Throwable {
Comparator<Integer> hostile = createHostileInstance();
var mh = MethodHandles.publicLookup()
.findVirtual(Integer.class, "compareTo", methodType(int.class, Integer.class));
@SuppressWarnings("unchecked")
Comparator<Integer> proxy = (Comparator<Integer>) asInterfaceInstance(Comparator.class, mh);
assertTrue(isWrapperInstance(proxy));
assertFalse(isWrapperInstance(hostile));
assertSame(mh, wrapperInstanceTarget(proxy));
assertThrows(IllegalArgumentException.class, () -> wrapperInstanceTarget(hostile));
assertSame(Comparator.class, wrapperInstanceType(proxy));
assertThrows(IllegalArgumentException.class, () -> wrapperInstanceType(hostile));
}
private static final String TYPE = "interfaceType";
private static final String TARGET = "target";
private static final ClassDesc CD_HostileWrapper = ClassDesc.of("HostileWrapper");
private static final ClassDesc CD_Comparator = ClassDesc.of("java.util.Comparator");
private static final MethodTypeDesc MTD_int_Object_Object = MethodTypeDesc.of(CD_int, CD_Object, CD_Object);
private static final MethodTypeDesc MTD_int_Integer = MethodTypeDesc.of(CD_int, CD_Integer);
// Update this template when the MHP template is updated
@SuppressWarnings("unchecked")
private Comparator<Integer> createHostileInstance() throws Throwable {
var cf = ClassFile.of();
var bytes = cf.build(CD_HostileWrapper, clb -> {
clb.withSuperclass(CD_Object);
clb.withFlags(ACC_FINAL | ACC_SYNTHETIC);
clb.withInterfaceSymbols(CD_Comparator);
// static and instance fields
clb.withField(TYPE, CD_Class, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
clb.withField(TARGET, CD_MethodHandle, ACC_PRIVATE | ACC_FINAL);
// <clinit>
clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
cob.loadConstant(CD_Comparator);
cob.putstatic(CD_HostileWrapper, TYPE, CD_Class);
cob.return_();
});
// <init>
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
cob.return_();
});
// implementation
clb.withMethodBody("compare", MTD_int_Object_Object, ACC_PUBLIC, cob -> {
cob.aload(1);
cob.checkcast(CD_Integer);
cob.aload(2);
cob.checkcast(CD_Integer);
cob.invokestatic(CD_Integer, "compareTo", MTD_int_Integer);
cob.ireturn();
});
});
var l = MethodHandles.lookup().defineHiddenClass(bytes, true);
return (Comparator<Integer>) l.findConstructor(l.lookupClass(), MethodType.methodType(void.class)).invoke();
}
/**
* Ensures a user interface cannot access a Proxy implementing it.
*/
@Test
public void testNoAccess() {
var instance = asInterfaceInstance(Client.class, MethodHandles.zero(void.class));
var instanceClass = instance.getClass();
var interfaceLookup = Client.lookup();
assertEquals(MethodHandles.Lookup.ORIGINAL, interfaceLookup.lookupModes() & MethodHandles.Lookup.ORIGINAL,
"Missing original flag on interface's lookup");
assertThrows(IllegalAccessException.class, () -> MethodHandles.privateLookupIn(instanceClass,
interfaceLookup));
}
/**
* Tests the Proxy module properties for Proxies implementing system and
* user interfaces.
*/
@ParameterizedTest
@ValueSource(classes = {Client.class, Runnable.class})
public void testModule(Class<?> ifaceClass) {
var mh = MethodHandles.zero(void.class);
var inst = asInterfaceInstance(ifaceClass, mh);
Module ifaceModule = ifaceClass.getModule();
Class<?> implClass = inst.getClass();
Module implModule = implClass.getModule();
String implPackage = implClass.getPackageName();
assertFalse(implModule.isExported(implPackage),
"implementation should not be exported");
assertTrue(ifaceModule.isExported(ifaceClass.getPackageName(), implModule),
"interface package should be exported to implementation");
assertTrue(implModule.isOpen(implPackage, MethodHandleProxies.class.getModule()),
"implementation class is not reflectively open to MHP class");
assertTrue(implModule.isNamed(), "dynamic module must be named");
assertTrue(implModule.getName().startsWith("jdk.MHProxy"),
() -> "incorrect dynamic module name: " + implModule.getName());
assertSame(ifaceClass.getClassLoader(), implClass.getClassLoader(),
"wrapper class should use the interface's loader ");
assertSame(implClass.getClassLoader(), implModule.getClassLoader(),
"module class loader should be wrapper class's loader");
}
/**
* Tests the access control of Proxies implementing system and user
* interfaces.
*/
@ParameterizedTest
@ValueSource(classes = {Client.class, Runnable.class})
public void testNoInstantiation(Class<?> ifaceClass) throws ReflectiveOperationException {
var mh = MethodHandles.zero(void.class);
var instanceClass = asInterfaceInstance(ifaceClass, mh).getClass();
var ctor = instanceClass.getDeclaredConstructor(MethodHandles.Lookup.class, MethodHandle.class, MethodHandle.class);
assertThrows(IllegalAccessException.class, () -> ctor.newInstance(Client.lookup(), mh, mh));
assertThrows(IllegalAccessException.class, () -> ctor.newInstance(MethodHandles.lookup(), mh, mh));
assertThrows(IllegalAccessException.class, () -> ctor.newInstance(MethodHandles.publicLookup(), mh, mh));
}
/**
* Tests the caching and weak reference of implementation classes for
* system and user interfaces.
*/
@ParameterizedTest
@ValueSource(classes = {Runnable.class, Client.class})
public void testWeakImplClass(Class<?> ifaceClass) {
var mh = MethodHandles.zero(void.class);
var wrapper1 = asInterfaceInstance(ifaceClass, mh);
var implClass = wrapper1.getClass();
System.gc(); // helps debug if incorrect items are weakly referenced
var wrapper2 = asInterfaceInstance(ifaceClass, mh);
assertSame(implClass, wrapper2.getClass(),
"MHP should reuse old implementation class when available");
var implClassRef = new WeakReference<>(implClass);
// clear strong references
implClass = null;
wrapper1 = null;
wrapper2 = null;
if (!ForceGC.wait(() -> implClassRef.refersTo(null))) {
fail("MHP impl class cannot be cleared by GC");
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2022, 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.
*/
module m1 {
requires m2;
requires org.junit.platform.console.standalone;
exports p1;
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2022, 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.
*/
package p1;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandleProxies;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.stream.Collectors;
import p2.TestIntf;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class Main {
public interface A {
default String aConcat(Object... objs) { return Arrays.deepToString(objs); }
}
public interface B {
default String bConcat(Object[] objs) { return Arrays.deepToString(objs); }
}
public interface C extends A, B {
String c(Object... objs);
}
private static String concat(Object... objs) {
return Arrays.stream(objs).map(Object::toString).collect(Collectors.joining());
}
/*
* Test the invocation of default methods with varargs
*/
@Test
public void testVarargsMethods() throws Throwable {
MethodHandle target = MethodHandles.lookup().findStatic(Main.class,
"concat", MethodType.methodType(String.class, Object[].class));
C proxy = MethodHandleProxies.asInterfaceInstance(C.class, target);
assertEquals("abc", proxy.c("a", "b", "c"));
assertEquals("[a, b, c]", proxy.aConcat("a", "b", "c"));
assertEquals("[a, b, c]", proxy.aConcat(new Object[] { "a", "b", "c" }));
assertEquals("[a, b, c]", proxy.bConcat(new Object[] { "a", "b", "c" }));
}
/*
* Test the invocation of a default method of an accessible interface
*/
@Test
public void modulePrivateInterface() {
MethodHandle target = MethodHandles.constant(String.class, "test");
TestIntf t = MethodHandleProxies.asInterfaceInstance(TestIntf.class, target);
assertEquals("test", t.test());
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2022, 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.
*/
module m2 {
exports p2 to m1;
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2022, 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 p2;
public interface TestIntf {
String test();
}

View file

@ -0,0 +1,535 @@
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.java.lang.invoke.MethodHandles;
import jdk.test.lib.TimeLimitedRunner;
import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;
import test.java.lang.invoke.lib.CodeCacheOverflowProcessor;
import test.java.lang.invoke.lib.Helper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/* @test
* @library /java/lang/invoke/common /test/lib
* @build jdk.test.lib.TimeLimitedRunner
* @compile CatchExceptionTest.java
* @run main/othervm -esa test.java.lang.invoke.MethodHandles.CatchExceptionTest
* @key intermittent randomness
*/
public class CatchExceptionTest {
private static final List<Class<?>> ARGS_CLASSES;
protected static final int MAX_ARITY = Helper.MAX_ARITY - 1;
static {
Class<?> classes[] = {
Object.class,
long.class,
int.class,
byte.class,
Integer[].class,
double[].class,
String.class,
};
ARGS_CLASSES = Collections.unmodifiableList(
Helper.randomClasses(classes, MAX_ARITY));
}
private final TestCase testCase;
private final int nargs;
private final int argsCount;
private final MethodHandle catcher;
private int dropped;
private MethodHandle thrower;
public CatchExceptionTest(TestCase testCase, final boolean isVararg,
final int argsCount, final int catchDrops) {
this.testCase = testCase;
this.dropped = catchDrops;
MethodHandle thrower = testCase.thrower;
int throwerLen = thrower.type().parameterCount();
List<Class<?>> classes;
int extra = Math.max(0, argsCount - throwerLen);
classes = getThrowerParams(isVararg, extra);
this.argsCount = throwerLen + classes.size();
thrower = Helper.addTrailingArgs(thrower, this.argsCount, classes);
if (isVararg && argsCount > throwerLen) {
MethodType mt = thrower.type();
Class<?> lastParam = mt.parameterType(mt.parameterCount() - 1);
thrower = thrower.asVarargsCollector(lastParam);
}
this.thrower = thrower;
this.dropped = Math.min(this.argsCount, catchDrops);
catcher = testCase.getCatcher(getCatcherParams());
nargs = Math.max(2, this.argsCount);
}
public static void main(String[] args) throws Throwable {
CodeCacheOverflowProcessor.runMHTest(CatchExceptionTest::test);
}
public static void test() throws Throwable {
System.out.println("classes = " + ARGS_CLASSES);
TestFactory factory = new TestFactory();
long timeout = Helper.IS_THOROUGH ? 0L : Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT);
// subtract vm init time and reserve time for vm exit
timeout *= 0.9;
TimeLimitedRunner runner = new TimeLimitedRunner(timeout, 2.0d,
() -> {
CatchExceptionTest test = factory.nextTest();
if (test != null) {
test.runTest();
return true;
}
return false;
});
for (CatchExceptionTest test : TestFactory.MANDATORY_TEST_CASES) {
test.runTest();
}
runner.call();
}
private List<Class<?>> getThrowerParams(boolean isVararg, int argsCount) {
return Helper.getParams(ARGS_CLASSES, isVararg, argsCount);
}
private List<Class<?>> getCatcherParams() {
int catchArgc = 1 + this.argsCount - dropped;
List<Class<?>> result = new ArrayList<>(
thrower.type().parameterList().subList(0, catchArgc - 1));
// prepend throwable
result.add(0, testCase.throwableClass);
return result;
}
private void runTest() {
if (Helper.IS_VERBOSE) {
System.out.printf("CatchException(%s, isVararg=%b argsCount=%d " +
"dropped=%d)%n",
testCase, thrower.isVarargsCollector(), argsCount, dropped);
}
Helper.clear();
Object[] args = Helper.randomArgs(
argsCount, thrower.type().parameterArray());
Object arg0 = Helper.MISSING_ARG;
Object arg1 = testCase.thrown;
if (argsCount > 0) {
arg0 = args[0];
}
if (argsCount > 1) {
args[1] = arg1;
}
Asserts.assertEQ(nargs, thrower.type().parameterCount());
if (argsCount < nargs) {
Object[] appendArgs = {arg0, arg1};
appendArgs = Arrays.copyOfRange(appendArgs, argsCount, nargs);
thrower = MethodHandles.insertArguments(
thrower, argsCount, appendArgs);
}
Asserts.assertEQ(argsCount, thrower.type().parameterCount());
MethodHandle target = MethodHandles.catchException(
testCase.filter(thrower), testCase.throwableClass,
testCase.filter(catcher));
Asserts.assertEQ(thrower.type(), target.type());
Asserts.assertEQ(argsCount, target.type().parameterCount());
Object returned;
try {
returned = target.invokeWithArguments(args);
} catch (Throwable ex) {
if (CodeCacheOverflowProcessor.isThrowableCausedByVME(ex)) {
// This error will be treated by CodeCacheOverflowProcessor
// to prevent the test from failing because of code cache overflow.
throw new Error(ex);
}
testCase.assertCatch(ex);
returned = ex;
}
testCase.assertReturn(returned, arg0, arg1, dropped, args);
}
}
class TestFactory {
public static final List<CatchExceptionTest> MANDATORY_TEST_CASES = new ArrayList<>();
private static final int MIN_TESTED_ARITY = 10;
static {
for (int[] args : new int[][]{
{0, 0},
{MIN_TESTED_ARITY, 0},
{MIN_TESTED_ARITY, MIN_TESTED_ARITY},
{CatchExceptionTest.MAX_ARITY, 0},
{CatchExceptionTest.MAX_ARITY, CatchExceptionTest.MAX_ARITY},
}) {
MANDATORY_TEST_CASES.addAll(createTests(args[0], args[1]));
}
}
private int count;
private int args;
private int dropArgs;
private int currentMaxDrops;
private int maxArgs;
private int maxDrops;
private int constructor;
private int constructorSize;
private boolean isVararg;
public TestFactory() {
if (Helper.IS_THOROUGH) {
maxArgs = maxDrops = CatchExceptionTest.MAX_ARITY;
} else {
maxArgs = MIN_TESTED_ARITY
+ Helper.RNG.nextInt(CatchExceptionTest.MAX_ARITY
- MIN_TESTED_ARITY)
+ 1;
maxDrops = MIN_TESTED_ARITY
+ Helper.RNG.nextInt(maxArgs - MIN_TESTED_ARITY)
+ 1;
args = 1;
}
System.out.printf("maxArgs = %d%nmaxDrops = %d%n", maxArgs, maxDrops);
constructorSize = TestCase.CONSTRUCTORS.size();
}
private static List<CatchExceptionTest> createTests(int argsCount,
int catchDrops) {
if (catchDrops > argsCount || argsCount < 0 || catchDrops < 0) {
throw new IllegalArgumentException("argsCount = " + argsCount
+ ", catchDrops = " + catchDrops
);
}
List<CatchExceptionTest> result = new ArrayList<>(
TestCase.CONSTRUCTORS.size());
for (Supplier<TestCase> constructor : TestCase.CONSTRUCTORS) {
result.add(new CatchExceptionTest(constructor.get(),
/* isVararg = */ true,
argsCount,
catchDrops));
result.add(new CatchExceptionTest(constructor.get(),
/* isVararg = */ false,
argsCount,
catchDrops));
}
return result;
}
/**
* @return next test from test matrix:
* {varArgs, noVarArgs} x TestCase.rtypes x TestCase.THROWABLES x {1, .., maxArgs } x {0, .., maxDrops}
*/
public CatchExceptionTest nextTest() {
if (constructor < constructorSize) {
return createTest();
}
constructor = 0;
count++;
if (!Helper.IS_THOROUGH && count > Helper.TEST_LIMIT) {
System.out.println("test limit is exceeded");
return null;
}
if (dropArgs <= currentMaxDrops) {
if (dropArgs == 0) {
if (Helper.IS_THOROUGH || Helper.RNG.nextBoolean()) {
++dropArgs;
return createTest();
} else if (Helper.IS_VERBOSE) {
System.out.printf(
"argsCount=%d : \"drop\" scenarios are skipped%n",
args);
}
} else {
++dropArgs;
return createTest();
}
}
if (args < maxArgs) {
dropArgs = 0;
currentMaxDrops = Math.min(args, maxDrops);
++args;
return createTest();
}
return null;
}
private CatchExceptionTest createTest() {
if (!Helper.IS_THOROUGH) {
return new CatchExceptionTest(
TestCase.CONSTRUCTORS.get(constructor++).get(),
Helper.RNG.nextBoolean(), args, dropArgs);
} else {
if (isVararg) {
isVararg = false;
return new CatchExceptionTest(
TestCase.CONSTRUCTORS.get(constructor++).get(),
isVararg, args, dropArgs);
} else {
isVararg = true;
return new CatchExceptionTest(
TestCase.CONSTRUCTORS.get(constructor).get(),
isVararg, args, dropArgs);
}
}
}
}
class TestCase<T> {
private static enum ThrowMode {
NOTHING,
CAUGHT,
UNCAUGHT,
ADAPTER
}
@SuppressWarnings("unchecked")
public static final List<Supplier<TestCase>> CONSTRUCTORS;
private static final MethodHandle FAKE_IDENTITY;
private static final MethodHandle THROW_OR_RETURN;
private static final MethodHandle CATCHER;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
THROW_OR_RETURN = lookup.findStatic(
TestCase.class,
"throwOrReturn",
MethodType.methodType(Object.class, Object.class,
Throwable.class)
);
CATCHER = lookup.findStatic(
TestCase.class,
"catcher",
MethodType.methodType(Object.class, Object.class));
FAKE_IDENTITY = lookup.findVirtual(
TestCase.class, "fakeIdentity",
MethodType.methodType(Object.class, Object.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new Error(e);
}
PartialConstructor[] constructors = {
create(Object.class, Object.class::cast),
create(String.class, Objects::toString),
create(int[].class, x -> new int[]{Objects.hashCode(x)}),
create(long.class,
x -> Objects.hashCode(x) & (-1L >>> 32)),
create(void.class, TestCase::noop)};
Throwable[] throwables = {
new ClassCastException("testing"),
new java.io.IOException("testing"),
new LinkageError("testing")};
List<Supplier<TestCase>> list = new ArrayList<>(constructors.length
* throwables.length * ThrowMode.values().length);
//noinspection unchecked
for (PartialConstructor f : constructors) {
for (ThrowMode mode : ThrowMode.values()) {
for (Throwable t : throwables) {
list.add(f.apply(mode, t));
}
}
}
CONSTRUCTORS = Collections.unmodifiableList(list);
}
public final Class<T> rtype;
public final ThrowMode throwMode;
public final Throwable thrown;
public final Class<? extends Throwable> throwableClass;
/**
* MH which takes 2 args (Object,Throwable), 1st is the return value,
* 2nd is the exception which will be thrown, if it's supposed in current
* {@link #throwMode}.
*/
public final MethodHandle thrower;
private final Function<Object, T> cast;
protected MethodHandle filter;
private int fakeIdentityCount;
private TestCase(Class<T> rtype, Function<Object, T> cast,
ThrowMode throwMode, Throwable thrown)
throws NoSuchMethodException, IllegalAccessException {
this.cast = cast;
filter = MethodHandles.lookup().findVirtual(
Function.class,
"apply",
MethodType.methodType(Object.class, Object.class))
.bindTo(cast);
this.rtype = rtype;
this.throwMode = throwMode;
this.throwableClass = thrown.getClass();
switch (throwMode) {
case NOTHING:
this.thrown = null;
break;
case ADAPTER:
case UNCAUGHT:
this.thrown = new Error("do not catch this");
break;
default:
this.thrown = thrown;
}
MethodHandle throwOrReturn = THROW_OR_RETURN;
if (throwMode == ThrowMode.ADAPTER) {
MethodHandle fakeIdentity = FAKE_IDENTITY.bindTo(this);
for (int i = 0; i < 10; ++i) {
throwOrReturn = MethodHandles.filterReturnValue(
throwOrReturn, fakeIdentity);
}
}
thrower = throwOrReturn.asType(MethodType.genericMethodType(2));
}
private static Void noop(Object x) {
return null;
}
private static <T2> PartialConstructor create(
Class<T2> rtype, Function<Object, T2> cast) {
return (t, u) -> () -> {
try {
return new TestCase<>(rtype, cast, t, u);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new Error(e);
}
};
}
private static <T extends Throwable>
Object throwOrReturn(Object normal, T exception) throws T {
if (exception != null) {
Helper.called("throwOrReturn/throw", normal, exception);
throw exception;
}
Helper.called("throwOrReturn/normal", normal, exception);
return normal;
}
private static <T extends Throwable>
Object catcher(Object o) {
Helper.called("catcher", o);
return o;
}
public MethodHandle filter(MethodHandle target) {
return MethodHandles.filterReturnValue(target, filter);
}
public MethodHandle getCatcher(List<Class<?>> classes) {
return MethodHandles.filterReturnValue(Helper.AS_LIST.asType(
MethodType.methodType(Object.class, classes)),
CATCHER
);
}
@Override
public String toString() {
return "TestCase{" +
"rtype=" + rtype +
", throwMode=" + throwMode +
", throwableClass=" + throwableClass +
'}';
}
public String callName() {
return "throwOrReturn/" +
(throwMode == ThrowMode.NOTHING
? "normal"
: "throw");
}
public void assertReturn(Object returned, Object arg0, Object arg1,
int catchDrops, Object... args) {
int lag = 0;
if (throwMode == ThrowMode.CAUGHT) {
lag = 1;
}
Helper.assertCalled(lag, callName(), arg0, arg1);
if (throwMode == ThrowMode.NOTHING) {
assertEQ(cast.apply(arg0), returned);
} else if (throwMode == ThrowMode.CAUGHT) {
List<Object> catchArgs = new ArrayList<>(Arrays.asList(args));
// catcher receives an initial subsequence of target arguments:
catchArgs.subList(args.length - catchDrops, args.length).clear();
// catcher also receives the exception, prepended:
catchArgs.add(0, thrown);
Helper.assertCalled("catcher", catchArgs);
assertEQ(cast.apply(catchArgs), returned);
}
Asserts.assertEQ(0, fakeIdentityCount);
}
private void assertEQ(T t, Object returned) {
if (rtype.isArray()) {
Asserts.assertEQ(t.getClass(), returned.getClass());
int n = Array.getLength(t);
Asserts.assertEQ(n, Array.getLength(returned));
for (int i = 0; i < n; ++i) {
Asserts.assertEQ(Array.get(t, i), Array.get(returned, i));
}
} else {
Asserts.assertEQ(t, returned);
}
}
private Object fakeIdentity(Object x) {
System.out.println("should throw through this!");
++fakeIdentityCount;
return x;
}
public void assertCatch(Throwable ex) {
try {
Asserts.assertSame(thrown, ex,
"must get the out-of-band exception");
} catch (Throwable t) {
ex.printStackTrace();
}
}
public interface PartialConstructor
extends BiFunction<ThrowMode, Throwable, Supplier<TestCase>> {
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2013, 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 8027823
* @run junit test.java.lang.invoke.TestCatchException
*/
package test.java.lang.invoke;
import java.lang.invoke.*;
import org.junit.*;
import static org.junit.Assert.*;
public class TestCatchException {
static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
static final MethodType M_TYPE = MethodType.methodType(int.class, Object.class, Object.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class);
private static int noThrow(Object o1, Object o2, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
return 42;
}
private static int throwEx(Object o1, Object o2, int i1, int i2, int i3, int i4, int i5, int i6, int i7) throws Exception {
throw new Exception();
}
private static int handler(Exception e) {
return 17;
}
@Test
public void testNoThrowPath() throws Throwable {
MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "noThrow", M_TYPE);
MethodHandle handler = LOOKUP.findStatic(TestCatchException.class, "handler", MethodType.methodType(int.class, Exception.class));
MethodHandle h = MethodHandles.catchException(target, Exception.class, handler);
int x = (int)h.invokeExact(new Object(), new Object(), 1, 2, 3, 4, 5, 6, 7);
assertEquals(x, 42);
}
@Test
public void testThrowPath() throws Throwable {
MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "throwEx", M_TYPE);
MethodHandle handler = LOOKUP.findStatic(TestCatchException.class, "handler", MethodType.methodType(int.class, Exception.class));
MethodHandle h = MethodHandles.catchException(target, Exception.class, handler);
int x = (int)h.invokeExact(new Object(), new Object(), 1, 2, 3, 4, 5, 6, 7);
assertEquals(x, 17);
}
static final Object masterParam = new Object();
static final Object[] masterTail = new Object[] { "str" };
static Exception masterEx = new Exception();
public static Object m1(Object o1, Object o2, Object o3, Object o4, Object o5,
Object o6, Object o7, Object o8, Object... tail) {
assertEquals(masterParam, o1);
assertEquals(masterParam, o2);
assertEquals(masterParam, o3);
assertEquals(masterParam, o4);
assertEquals(masterParam, o5);
assertEquals(masterParam, o6);
assertEquals(masterParam, o7);
assertEquals(masterParam, o8);
assertEquals(masterTail, tail);
return tail;
}
public static Object m2(Exception e, Object o1, Object o2, Object o3, Object o4,
Object o5, Object o6, Object o7, Object o8, Object... tail) {
assertEquals(masterEx, e);
assertEquals(masterParam, o1);
assertEquals(masterParam, o2);
assertEquals(masterParam, o3);
assertEquals(masterParam, o4);
assertEquals(masterParam, o5);
assertEquals(masterParam, o6);
assertEquals(masterParam, o7);
assertEquals(masterParam, o8);
assertEquals(masterTail, tail);
return tail;
}
public static Object throwEx(Object o1, Object o2, Object o3, Object o4, Object o5,
Object o6, Object o7, Object o8, Object... tail) throws Exception {
assertEquals(masterParam, o1);
assertEquals(masterParam, o2);
assertEquals(masterParam, o3);
assertEquals(masterParam, o4);
assertEquals(masterParam, o5);
assertEquals(masterParam, o6);
assertEquals(masterParam, o7);
assertEquals(masterParam, o8);
assertEquals(masterTail, tail);
throw masterEx;
}
@Test
public void testVarargsCollectorNoThrow() throws Throwable {
MethodType t1 = MethodType.methodType(Object.class, Object.class, Object.class, Object.class, Object.class,
Object.class, Object.class, Object.class, Object.class, Object[].class);
MethodType t2 = t1.insertParameterTypes(0, Exception.class);
MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "m1", t1)
.asVarargsCollector(Object[].class);
MethodHandle catcher = LOOKUP.findStatic(TestCatchException.class, "m2", t2)
.asVarargsCollector(Object[].class);
MethodHandle gwc = MethodHandles.catchException(target, Exception.class, catcher);
Object o = masterParam;
Object[] obj1 = masterTail;
Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1);
assertEquals(r2, obj1);
}
@Test
public void testVarargsCollectorThrow() throws Throwable {
MethodType t1 = MethodType.methodType(Object.class, Object.class, Object.class, Object.class, Object.class,
Object.class, Object.class, Object.class, Object.class, Object[].class);
MethodType t2 = t1.insertParameterTypes(0, Exception.class);
MethodHandle target = LOOKUP.findStatic(TestCatchException.class, "throwEx", t1)
.asVarargsCollector(Object[].class);
MethodHandle catcher = LOOKUP.findStatic(TestCatchException.class, "m2", t2)
.asVarargsCollector(Object[].class);
MethodHandle gwc = MethodHandles.catchException(target, Exception.class, catcher);
Object o = masterParam;
Object[] obj1 = masterTail;
Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1);
assertEquals(r2, obj1);
}
public static void main(String[] args) throws Throwable {
TestCatchException test = new TestCatchException();
test.testNoThrowPath();
test.testThrowPath();
test.testVarargsCollectorNoThrow();
test.testVarargsCollectorThrow();
System.out.println("TEST PASSED");
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, 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 8255398
* @run junit TestDropReturn
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import static java.lang.invoke.MethodType.methodType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class TestDropReturn {
@ParameterizedTest
@MethodSource("dropReturnCases")
public void testDropReturn(Class<?> cls, Object testValue) throws Throwable {
MethodHandle mh = MethodHandles.identity(cls);
assertEquals(methodType(cls, cls), mh.type());
Object x = mh.invoke(testValue);
assertEquals(testValue, x);
mh = MethodHandles.dropReturn(mh);
assertEquals(methodType(void.class, cls), mh.type());
mh.invoke(testValue); // should at least work
}
public static Object[][] dropReturnCases() {
return new Object[][]{
{ boolean.class, true },
{ byte.class, (byte) 10 },
{ char.class, 'x' },
{ short.class, (short) 10 },
{ int.class, 10 },
{ long.class, 10L },
{ float.class, 10F },
{ double.class, 10D },
{ Object.class, new Object() },
{ String.class, "ABCD" },
};
}
}

View file

@ -0,0 +1,234 @@
/*
* 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
* @run junit/othervm -Xverify:all TestTableSwitch
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
public class TestTableSwitch {
static final MethodHandle MH_IntConsumer_accept;
static final MethodHandle MH_check;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MH_IntConsumer_accept = lookup.findVirtual(IntConsumer.class, "accept",
MethodType.methodType(void.class, int.class));
MH_check = lookup.findStatic(TestTableSwitch.class, "check",
MethodType.methodType(void.class, List.class, Object[].class));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static MethodHandle simpleTestCase(String value) {
return simpleTestCase(String.class, value);
}
public static MethodHandle simpleTestCase(Class<?> type, Object value) {
return MethodHandles.dropArguments(MethodHandles.constant(type, value), 0, int.class);
}
public static Object testValue(Class<?> type) {
if (type == String.class) {
return "X";
} else if (type == byte.class) {
return (byte) 42;
} else if (type == short.class) {
return (short) 84;
} else if (type == char.class) {
return 'Y';
} else if (type == int.class) {
return 168;
} else if (type == long.class) {
return 336L;
} else if (type == float.class) {
return 42F;
} else if (type == double.class) {
return 84D;
} else if (type == boolean.class) {
return true;
}
return null;
}
static final Class<?>[] TEST_TYPES = {
Object.class,
String.class,
byte.class,
short.class,
char.class,
int.class,
long.class,
float.class,
double.class,
boolean.class
};
public static Object[] testArguments(int caseNum, List<Object> testValues) {
Object[] args = new Object[testValues.size() + 1];
args[0] = caseNum;
int insertPos = 1;
for (Object testValue : testValues) {
args[insertPos++] = testValue;
}
return args;
}
public static Object[][] nonVoidCases() {
List<Object[]> tests = new ArrayList<>();
for (Class<?> returnType : TEST_TYPES) {
for (int numCases = 1; numCases < 5; numCases++) {
tests.add(new Object[] { returnType, numCases, List.of() });
tests.add(new Object[] { returnType, numCases, List.of(TEST_TYPES) });
}
}
return tests.toArray(Object[][]::new);
}
private static void check(List<Object> testValues, Object[] collectedValues) {
assertArrayEquals(testValues.toArray(), collectedValues);
}
@ParameterizedTest
@MethodSource("nonVoidCases")
public void testNonVoidHandles(Class<?> type, int numCases, List<Class<?>> additionalTypes) throws Throwable {
MethodHandle collector = MH_check;
List<Object> testArguments = new ArrayList<>();
collector = MethodHandles.insertArguments(collector, 0, testArguments);
collector = collector.asCollector(Object[].class, additionalTypes.size());
Object defaultReturnValue = testValue(type);
MethodHandle defaultCase = simpleTestCase(type, defaultReturnValue);
defaultCase = MethodHandles.collectArguments(defaultCase, 1, collector);
Object[] returnValues = new Object[numCases];
MethodHandle[] cases = new MethodHandle[numCases];
for (int i = 0; i < cases.length; i++) {
Object returnValue = testValue(type);
returnValues[i] = returnValue;
MethodHandle theCase = simpleTestCase(type, returnValue);
theCase = MethodHandles.collectArguments(theCase, 1, collector);
cases[i] = theCase;
}
MethodHandle mhSwitch = MethodHandles.tableSwitch(
defaultCase,
cases
);
for (Class<?> additionalType : additionalTypes) {
testArguments.add(testValue(additionalType));
}
assertEquals(defaultReturnValue, mhSwitch.invokeWithArguments(testArguments(-1, testArguments)));
for (int i = 0; i < numCases; i++) {
assertEquals(returnValues[i], mhSwitch.invokeWithArguments(testArguments(i, testArguments)));
}
assertEquals(defaultReturnValue, mhSwitch.invokeWithArguments(testArguments(numCases, testArguments)));
}
@Test
public void testVoidHandles() throws Throwable {
IntFunction<MethodHandle> makeTestCase = expectedIndex -> {
IntConsumer test = actualIndex -> assertEquals(expectedIndex, actualIndex);
return MH_IntConsumer_accept.bindTo(test);
};
MethodHandle mhSwitch = MethodHandles.tableSwitch(
/* default: */ makeTestCase.apply(-1),
/* case 0: */ makeTestCase.apply(0),
/* case 1: */ makeTestCase.apply(1),
/* case 2: */ makeTestCase.apply(2)
);
mhSwitch.invokeExact((int) -1);
mhSwitch.invokeExact((int) 0);
mhSwitch.invokeExact((int) 1);
mhSwitch.invokeExact((int) 2);
}
@Test
public void testNullDefaultHandle() {
assertThrows(NullPointerException.class, () -> MethodHandles.tableSwitch(null, simpleTestCase("test")));
}
@Test
public void testNullCases() {
MethodHandle[] cases = null;
assertThrows(NullPointerException.class, () ->
MethodHandles.tableSwitch(simpleTestCase("default"), cases));
}
@Test
public void testNullCase() {
assertThrows(NullPointerException.class, () -> MethodHandles.tableSwitch(simpleTestCase("default"), simpleTestCase("case"), null));
}
@Test
public void testNotEnoughCases() {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.tableSwitch(simpleTestCase("default")));
}
@Test
public void testNotEnoughParameters() {
MethodHandle empty = MethodHandles.empty(MethodType.methodType(void.class));
assertThrows(IllegalArgumentException.class, () ->
MethodHandles.tableSwitch(empty, empty, empty));
}
@Test
public void testNoLeadingIntParameter() {
MethodHandle empty = MethodHandles.empty(MethodType.methodType(void.class, double.class));
assertThrows(IllegalArgumentException.class, () ->
MethodHandles.tableSwitch(empty, empty, empty));
}
@Test
public void testWrongCaseType() {
// doesn't return a String
MethodHandle wrongType = MethodHandles.empty(MethodType.methodType(void.class, int.class));
assertThrows(IllegalArgumentException.class, () ->
MethodHandles.tableSwitch(simpleTestCase("default"), simpleTestCase("case"), wrongType));
}
}

View file

@ -0,0 +1,509 @@
/*
* Copyright (c) 2020, 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 8230501
* @library /test/lib
* @run junit/othervm ClassDataTest
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.classfile.ClassBuilder;
import java.lang.classfile.ClassFile;
import java.lang.classfile.TypeKind;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.DirectMethodHandleDesc;
import java.lang.constant.DynamicConstantDesc;
import java.lang.constant.MethodTypeDesc;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.AccessFlag;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static java.lang.classfile.ClassFile.*;
import static java.lang.constant.ConstantDescs.*;
import static java.lang.invoke.MethodHandles.Lookup.*;
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 ClassDataTest {
private static final Lookup LOOKUP = MethodHandles.lookup();
private static final ClassDesc CD_ClassDataTest = ClassDataTest.class.describeConstable().orElseThrow();
@Test
public void testOriginalAccess() throws IllegalAccessException {
Lookup lookup = hiddenClass(20);
assertTrue(lookup.hasFullPrivilegeAccess());
int value = MethodHandles.classData(lookup, "_", int.class);
assertEquals(20, value);
Integer i = MethodHandles.classData(lookup, "_", Integer.class);
assertEquals(20, i.intValue());
}
/*
* A lookup class with no class data.
*/
@Test
public void noClassData() throws IllegalAccessException {
assertNull(MethodHandles.classData(LOOKUP, "_", Object.class));
}
private static Object[][] teleportedLookup() throws ReflectiveOperationException {
Lookup lookup = hiddenClass(30);
Class<?> hc = lookup.lookupClass();
assertClassData(lookup, 30);
int fullAccess = PUBLIC|PROTECTED|PACKAGE|MODULE|PRIVATE;
return new Object[][] {
new Object[] { MethodHandles.privateLookupIn(hc, LOOKUP), fullAccess},
new Object[] { LOOKUP.in(hc), fullAccess & ~(PROTECTED|PRIVATE) },
new Object[] { lookup.dropLookupMode(PRIVATE), fullAccess & ~(PROTECTED|PRIVATE) },
};
}
@ParameterizedTest
@MethodSource("teleportedLookup")
public void illegalAccess(Lookup lookup, int access) throws IllegalAccessException {
int lookupModes = lookup.lookupModes();
assertEquals(0, lookupModes & ORIGINAL);
assertEquals(access, lookupModes);
assertThrows(IllegalAccessException.class, () -> MethodHandles.classData(lookup, "_", int.class));
}
@Test
public void incorrectType() throws IllegalAccessException {
Lookup lookup = hiddenClass(20);
assertThrows(ClassCastException.class, () -> MethodHandles.classData(lookup, "_", Long.class));
}
@Test
public void invalidIndex() throws IllegalAccessException {
Lookup lookup = hiddenClass(List.of());
assertThrows(IndexOutOfBoundsException.class, () -> MethodHandles.classDataAt(lookup, "_", Object.class, 0));
}
@Test
public void unboxNull() throws IllegalAccessException {
List<Integer> list = new ArrayList<>();
list.add(null);
Lookup lookup = hiddenClass(list);
assertThrows(NullPointerException.class, () -> MethodHandles.classDataAt(lookup, "_", int.class, 0));
}
@Test
public void nullElement() throws IllegalAccessException {
List<Object> list = new ArrayList<>();
list.add(null);
Lookup lookup = hiddenClass(list);
assertNull(MethodHandles.classDataAt(lookup, "_", Object.class, 0));
}
@Test
public void intClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T1-int");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 100, true);
int value = MethodHandles.classData(lookup, "_", int.class);
assertEquals(100, value);
// call through condy
assertClassData(lookup, 100);
}
@Test
public void floatClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T1-float");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, float.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 0.1234f, true);
float value = MethodHandles.classData(lookup, "_", float.class);
assertEquals(0.1234f, value);
// call through condy
assertClassData(lookup, 0.1234f);
}
@Test
public void classClassData() throws ReflectiveOperationException {
Class<?> hc = hiddenClass(100).lookupClass();
ClassByteBuilder builder = new ClassByteBuilder("T2");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
assertEquals(hc, value);
// call through condy
assertClassData(lookup, hc);
}
@Test
public void arrayClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T3");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, String[].class).build();
String[] colors = new String[] { "red", "yellow", "blue"};
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, colors, true);
assertClassData(lookup, colors.clone());
// class data is modifiable and not a constant
colors[0] = "black";
// it will get back the modified class data
String[] value = MethodHandles.classData(lookup, "_", String[].class);
assertArrayEquals(colors, value);
// even call through condy as it's not a constant
assertClassData(lookup, colors);
}
@Test
public void listClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T4");
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 2).build();
List<Integer> cd = List.of(100, 101, 102, 103);
int expected = 102; // element at index=2
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
assertEquals(expected, value);
// call through condy
assertClassData(lookup, expected);
}
@Test
public void arrayListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T4");
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 1).build();
ArrayList<Integer> cd = new ArrayList<>();
Stream.of(100, 101, 102, 103).forEach(cd::add);
int expected = 101; // element at index=1
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 1);
assertEquals(expected, value);
// call through condy
assertClassData(lookup, expected);
}
private static Lookup hiddenClass(int value) {
ClassByteBuilder builder = new ClassByteBuilder("HC");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
return assertDoesNotThrow(() -> LOOKUP.defineHiddenClassWithClassData(bytes, value, true));
}
private static Lookup hiddenClass(List<?> list) {
ClassByteBuilder builder = new ClassByteBuilder("HC");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, List.class).build();
return assertDoesNotThrow(() -> LOOKUP.defineHiddenClassWithClassData(bytes, list, true));
}
@Test
public void condyInvokedFromVirtualMethod() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T5");
// generate classData instance method
byte[] bytes = builder.classData(ACC_PUBLIC, Class.class).build();
Lookup hcLookup = hiddenClass(100);
assertClassData(hcLookup, 100);
Class<?> hc = hcLookup.lookupClass();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
assertEquals(hc, value);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance(), hc);
}
@Test
public void immutableListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T6");
// generate classDataAt instance method
byte[] bytes = builder.classDataAt(ACC_PUBLIC, Integer.class, 2).build();
List<Integer> cd = List.of(100, 101, 102, 103);
int expected = 102; // element at index=2
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
assertEquals(expected, value);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance() ,expected);
}
/*
* The return value of MethodHandles::classDataAt is the element
* contained in the list when the method is called.
* If MethodHandles::classDataAt is called via condy, the value
* will be captured as a constant. If the class data is modified
* after the element at the given index is computed via condy,
* subsequent LDC of such ConstantDynamic entry will return the same
* value. However, direct invocation of MethodHandles::classDataAt
* will return the modified value.
*/
@Test
public void mutableListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T7");
// generate classDataAt instance method
byte[] bytes = builder.classDataAt(ACC_PUBLIC, MethodType.class, 0).build();
MethodType mtype = MethodType.methodType(int.class, String.class);
List<MethodType> cd = new ArrayList<>(List.of(mtype));
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance(), mtype);
// modify the class data
assertSame(mtype, cd.remove(0));
cd.add(0, MethodType.methodType(void.class));
MethodType newMType = cd.get(0);
// loading the element using condy returns the original value
assertClassData(lookup, c.newInstance(), mtype);
// direct invocation of MethodHandles.classDataAt returns the modified value
assertEquals(newMType, MethodHandles.classDataAt(lookup, "_", MethodType.class, 0));
}
// helper method to extract from a class data map
public static <T> T getClassDataEntry(Lookup lookup, String key, Class<T> type) throws IllegalAccessException {
Map<String, T> cd = MethodHandles.classData(lookup, "_", Map.class);
return type.cast(cd.get(key));
}
@Test
public void classDataMap() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("map");
// generate classData static method
DirectMethodHandleDesc bsm = ConstantDescs.ofConstantBootstrap(CD_ClassDataTest, "getClassDataEntry", CD_Object);
// generate two accessor methods to get the entries from class data
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Map.class)
.classData(ACC_PUBLIC|ACC_STATIC, "getClass",
Class.class, DynamicConstantDesc.ofNamed(bsm, "class", CD_Class))
.classData(ACC_PUBLIC|ACC_STATIC, "getMethod",
MethodHandle.class, DynamicConstantDesc.ofNamed(bsm, "method", CD_MethodHandle))
.build();
// generate a hidden class
Lookup hcLookup = hiddenClass(100);
Class<?> hc = hcLookup.lookupClass();
assertClassData(hcLookup, 100);
MethodHandle mh = hcLookup.findStatic(hc, "classData", MethodType.methodType(int.class));
Map<String, Object> cd = Map.of("class", hc, "method", mh);
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
assertClassData(lookup, cd);
// validate the entries from the class data map
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("getClass");
Class<?> v = (Class<?>)m.invoke(null);
assertEquals(hc, v);
Method m1 = c.getMethod("getMethod");
MethodHandle v1 = (MethodHandle) m1.invoke(null);
assertEquals(mh, v1);
}
@Test
public void nonDefaultName() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("nonDefaultName");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class)
.build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, ClassDataTest.class, true);
assertClassData(lookup, ClassDataTest.class);
// throw IAE
assertThrows(IllegalArgumentException.class, () -> MethodHandles.classData(lookup, "non_default_name", Class.class));
}
static class ClassByteBuilder {
private static final String OBJECT_CLS = "java/lang/Object";
private static final String MHS_CLS = "java/lang/invoke/MethodHandles";
private static final String CLASS_DATA_BSM_DESCR =
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;";
private Consumer<ClassBuilder> cw;
private final ClassDesc classname;
/**
* A builder to generate a class file to access class data
* @param classname
*/
ClassByteBuilder(String classname) {
this.classname = ClassDesc.ofInternalName(classname);
this.cw = clb -> {
clb.withSuperclass(CD_Object);
clb.withFlags(AccessFlag.FINAL);
clb.withMethodBody(INIT_NAME, MTD_void, ACC_PUBLIC, cob -> {
cob.aload(0);
cob.invokespecial(CD_Object, INIT_NAME, MTD_void);
cob.return_();
});
};
}
byte[] build() {
byte[] bytes = ClassFile.of().build(classname, cw);
Path p = Paths.get(classname + ".class");
try (OutputStream os = Files.newOutputStream(p)) {
os.write(bytes);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return bytes;
}
/*
* Generate classData method to load class data via condy
*/
ClassByteBuilder classData(int accessFlags, Class<?> returnType) {
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
cw = cw.andThen(clb -> {
clb.withMethodBody("classData", mt, accessFlags, cob -> {
cob.loadConstant(DynamicConstantDesc.ofNamed(BSM_CLASS_DATA, DEFAULT_NAME, returnDesc));
cob.return_(TypeKind.from(returnType));
});
});
return this;
}
/*
* Generate classDataAt method to load an element from class data via condy
*/
ClassByteBuilder classDataAt(int accessFlags, Class<?> returnType, int index) {
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
cw = cw.andThen(clb -> {
clb.withMethodBody("classData", mt, accessFlags, cob -> {
cob.loadConstant(DynamicConstantDesc.ofNamed(BSM_CLASS_DATA_AT, DEFAULT_NAME, returnDesc, index));
cob.return_(TypeKind.from(returnType));
});
});
return this;
}
ClassByteBuilder classData(int accessFlags, String name, Class<?> returnType, DynamicConstantDesc<?> dynamic) {
ClassDesc returnDesc = returnType.describeConstable().orElseThrow();
MethodTypeDesc mt = MethodTypeDesc.of(returnDesc);
cw = cw.andThen(clb -> {
clb.withMethodBody(name, mt, accessFlags, cob -> {
cob.loadConstant(dynamic);
cob.return_(TypeKind.from(returnType));
});
});
return this;
}
}
/*
* Load an int constant from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, int value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
int v = (int) m.invoke(null);
assertEquals(value, v);
}
/*
* Load an int constant from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, Object o, int value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
int v = (int) m.invoke(o);
assertEquals(value, v);
}
/*
* Load a float constant from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, float value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
float v = (float) m.invoke(null);
assertEquals(value, v);
}
/*
* Load a Class constant from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, Class<?> value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Class<?> v = (Class<?>)m.invoke(null);
assertEquals(value, v);
}
/*
* Load a Class from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, Object o, Class<?> value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(o);
assertEquals(value, v);
}
/*
* Load an Object from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, Object value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(null);
assertEquals(value, v);
}
/*
* Load an Object array from class data via condy and
* verify it matches the given value in content.
*/
private static void assertClassData(Lookup lookup, Object[] value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(null);
assertArrayEquals(value, (Object[]) v);
}
/*
* Load an Object from class data via condy and
* verify it matches the given value.
*/
private static void assertClassData(Lookup lookup, Object o, Object value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(o);
assertEquals(value, v);
}
}

View file

@ -0,0 +1,79 @@
/*
* 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.
*/
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
/**
* @test
* @bug 8235521
* @summary Tests for Lookup::ensureClassInitialized
* @build java.base/* m1/* m2/* Main
* @run junit/othervm --add-modules m1 Main
*/
public class Main {
// Test access to public java.lang class
@Test
public void testPublic() throws Exception {
assertFalse(Helper.isInitialized(PublicInit.class));
MethodHandles.lookup().ensureInitialized(PublicInit.class);
assertTrue(Helper.isInitialized(PublicInit.class));
// no-op if already initialized
MethodHandles.lookup().ensureInitialized(PublicInit.class);
}
// access denied to package-private java.lang class
@Test
public void testPackagePrivate() throws Exception {
Class<?> c = Class.forName("java.lang.DefaultInit", false, null);
assertFalse(Helper.isInitialized(c));
// access denied
assertThrows(IllegalAccessException.class, () -> MethodHandles.lookup().ensureInitialized(c));
}
// access denied to public class in a non-exported package
@Test
public void testNonExportedPackage() throws Exception {
Class<?> c = Class.forName("jdk.internal.misc.VM", false, null);
// access denied
assertThrows(IllegalAccessException.class, () -> MethodHandles.lookup().ensureInitialized(c));
}
// invoke p1.Test::test to test module boundary access
@Test
public void testModuleAccess() throws Exception {
Class<?> c = Class.forName("p1.Test");
Method m = c.getMethod("test");
m.invoke(null);
}
@Test
public void testArrayType() throws Exception {
Class<?> arrayType = PublicInit.class.arrayType();
assertThrows(IllegalArgumentException.class, () -> MethodHandles.lookup().ensureInitialized(arrayType));
}
}

View file

@ -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.
*/
package java.lang;
class DefaultInit {
private static boolean initialized;
static {
initialized = true;
}
}

View file

@ -0,0 +1,32 @@
/*
* 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.
*/
package java.lang;
import jdk.internal.misc.Unsafe;
public class Helper {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
public static boolean isInitialized(Class<?> c) {
return !UNSAFE.shouldBeInitialized(c);
}
}

View file

@ -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.
*/
package java.lang;
public class PublicInit {
private static boolean initialized;
static {
initialized = true;
}
}

View file

@ -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.
*/
module m1 {
requires m2;
exports p1;
}

View file

@ -0,0 +1,38 @@
/*
* 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.
*/
package p1;
import java.lang.invoke.MethodHandles;
public class A {
static final Object lock;
static {
try {
MethodHandles.lookup().ensureInitialized(A.class);
lock = new Object();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package p1;
class B {
private static final B INSTANCE = new B();
}

View file

@ -0,0 +1,152 @@
/*
* 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.
*/
package p1;
import p1.internal.*;
import static java.lang.invoke.MethodHandles.Lookup.*;
import java.lang.invoke.MethodHandles;
/*
* Test::test is invoked by Main class
*/
public class Test {
public static void test() throws Exception {
clinitInvokeEnsureClassInitialized();
testModuleAccess();
testPackageAccess();
testFullPrivilegeAccess();
}
/*
* A::<clinit> calls Lookup::ensureClassInitialized(A.class)
*/
public static void clinitInvokeEnsureClassInitialized() throws Exception {
assertFalse(Helper.isInitialized(A.class));
// same package and public type
MethodHandles.lookup().ensureInitialized(A.class);
assertTrue(Helper.isInitialized(A.class));
}
/*
* Test Lookup with MODULE access to initialize class in this module
* and another module it reads.
*/
public static void testModuleAccess() throws Exception {
MethodHandles.Lookup lookup = MethodHandles.lookup().dropLookupMode(PACKAGE);
assertTrue((lookup.lookupModes() & PACKAGE) == 0);
assertFalse(Helper.isInitialized(p2.T.class));
// exported by another module
lookup.ensureInitialized(p2.T.class);
assertTrue(Helper.isInitialized(p2.T.class));
// non-exported type in another module
Class<?> x = Class.forName(p2.T.class.getModule(), "p2.internal.X");
assertFalse(Helper.isInitialized(x));
try {
lookup.ensureInitialized(x);
throw new AssertionError(lookup + " should not have access to " + x);
} catch (IllegalAccessException e) {}
// no access to package-private class in the same module
try {
lookup.ensureInitialized(E.class);
throw new AssertionError(lookup + " should not have access to " + E.class);
} catch (IllegalAccessException e) {}
}
/*
* Test Lookup with PACKAGE access
*/
public static void testPackageAccess() throws Exception {
MethodHandles.Lookup lookup = MethodHandles.lookup().dropLookupMode(PRIVATE);
assertTrue((lookup.lookupModes() & PRIVATE) == 0);
// verify these classes are not initialized
assertFalse(Helper.isInitialized(B.class));
assertFalse(Helper.isInitialized(C.class));
assertFalse(Helper.isInitialized(E.class));
// same package but package-private
lookup.ensureInitialized(B.class);
assertTrue(Helper.isInitialized(B.class));
// different package and public type
lookup.ensureInitialized(C.class);
assertTrue(Helper.isInitialized(C.class));
// different package and non-public types
Class<?> d = Class.forName(Test.class.getModule(), "p1.internal.D");
assertFalse(Helper.isInitialized(d));
try {
lookup.ensureInitialized(d);
throw new AssertionError(lookup + " should not have access to " + d);
} catch (IllegalAccessException e) {}
}
/*
* Test Lookup with full privilege access
*/
public static void testFullPrivilegeAccess() throws Exception {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// these classes have been initialized
assertTrue(Helper.isInitialized(A.class)); // same package and public type
assertTrue(Helper.isInitialized(B.class)); // same package but package-private
assertTrue(Helper.isInitialized(C.class)); // different package and public type
// verify access to these classes
lookup.ensureInitialized(A.class);
lookup.ensureInitialized(B.class);
lookup.ensureInitialized(C.class);
// different package and non-public types
Class<?> d = Class.forName(Test.class.getModule(), "p1.internal.D");
try {
assertFalse(Helper.isInitialized(d));
lookup.ensureInitialized(d);
throw new AssertionError(lookup + " should not have access to " + d);
} catch (IllegalAccessException e) {}
// nestmate and same package
assertFalse(Helper.isInitialized(E.class));
assertTrue(Test.class.isNestmateOf(E.class));
lookup.ensureInitialized(E.class);
}
public static void assertTrue(boolean v) {
if (!v) {
throw new AssertionError("unexpected result");
}
}
public static void assertFalse(boolean v) {
if (v) {
throw new AssertionError("unexpected result");
}
}
// nestmate
static class E {
}
}

View file

@ -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.
*/
package p1.internal;
public class C {
}

View file

@ -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.
*/
package p1.internal;
class D {
}

View file

@ -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.
*/
module m2 {
exports p2 to m1;
}

View file

@ -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.
*/
package p2;
public class T {
}

View file

@ -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.
*/
package p2.internal;
public class X {
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, 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
* @build test/* m1/* m2/* m3/* Unnamed
* @run junit/othervm test/p.PrivateLookupInTests
* @summary Unit tests for MethodHandles.privateLookupIn
*/

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2017, 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 Unnamed { }

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
open module m1 {
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p1;
class Type {
private static final Object obj = new Object();
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
module m2 {
opens p2 to test;
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p2;
class Type {
private static final Object obj = new Object();
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
module m3 {
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p3;
class Type {
private static final Object obj = new Object();
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, 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.
*/
module test {
requires org.junit.platform.console.standalone;
exports p to org.junit.platform.console.standalone; // JUnit invokes the public methods in PrivateLookupInTests
}

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2016, 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.
*/
package p;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Modifier;
import static java.lang.invoke.MethodHandles.Lookup.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* Unit tests for MethodHandles.privateLookupIn
*/
public class PrivateLookupInTests {
/**
* A public and non-public types in the test module but in a different
* package to the test class.
*
* package p.internal;
* public class PublicType {
* }
*
* package p.internal;
* class NonPublicType {
* private static final Object obj = ...
* }
*/
private static Class<?> publicType;
private static Class<?> nonPublicType;
// initialize and sanity check publicType/nonPublicType
@BeforeAll
public static void init() throws Exception {
publicType = Class.forName("p.internal.PublicType");
assertSame(PrivateLookupInTests.class.getModule(), publicType.getModule());
assertNotEquals(publicType.getPackageName(), PrivateLookupInTests.class.getPackageName());
assertTrue(Modifier.isPublic(publicType.getModifiers()));
nonPublicType = Class.forName("p.internal.NonPublicType");
assertSame(PrivateLookupInTests.class.getModule(), nonPublicType.getModule());
assertNotEquals(nonPublicType.getPackageName(), PrivateLookupInTests.class.getPackageName());
assertFalse(Modifier.isPublic(nonPublicType.getModifiers()));
}
// Invoke MethodHandles.privateLookupIn with a full-power caller
@Test
public void testAllAccessCallerSameModule() throws Throwable {
Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, MethodHandles.lookup());
assertTrue(lookup.lookupClass() == nonPublicType);
assertTrue(lookup.hasFullPrivilegeAccess());
assertTrue((lookup.lookupModes() & ORIGINAL) == 0);
// get obj field
MethodHandle mh = lookup.findStaticGetter(nonPublicType, "obj", Object.class);
Object obj = mh.invokeExact();
}
// Invoke MethodHandles.privateLookupIn with a reduced-power caller
@Test
public void testReducedAccessCallerSameModule() throws Throwable {
Lookup caller = MethodHandles.lookup().dropLookupMode(PACKAGE);
assertEquals(0, caller.lookupModes() & PRIVATE);
assertEquals(0, caller.lookupModes() & PACKAGE);
assertNotEquals(0, caller.lookupModes() & MODULE);
assertEquals(0, caller.lookupModes() & ORIGINAL);
assertThrows(IllegalAccessException.class, () ->
MethodHandles.privateLookupIn(nonPublicType, caller));
}
// Invoke MethodHandles.privateLookupIn with the public lookup as caller
@Test
public void testPublicLookupSameModule() throws Exception {
Lookup caller = MethodHandles.publicLookup();
assertThrows(IllegalAccessException.class, () ->
MethodHandles.privateLookupIn(publicType, caller));
}
// test reads m1, open module m1 containing p1
@Test
public void testTargetClassInOpenModule() throws Throwable {
// m1/p1.Type
Class<?> clazz = Class.forName("p1.Type");
assertEquals("m1", clazz.getModule().getName());
// ensure that this module reads m1
Module thisModule = getClass().getModule();
Module m1 = clazz.getModule();
thisModule.addReads(clazz.getModule());
assertTrue(m1.isOpen("p1", thisModule));
Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
assertSame(clazz, lookup.lookupClass());
assertEquals(PRIVATE, lookup.lookupModes() & PRIVATE);
assertEquals(0, lookup.lookupModes() & MODULE);
// get obj field
MethodHandle mh = lookup.findStaticGetter(clazz, "obj", Object.class);
Object obj = mh.invokeExact();
}
// test target class in unnamed module
@Test
public void testTargetClassInUnnamedModule() throws Throwable {
Class<?> clazz = Class.forName("Unnamed");
assertFalse(clazz.getModule().isNamed());
// thisModule does not read the unnamed module
Module thisModule = getClass().getModule();
assertFalse(thisModule.canRead(clazz.getModule()));
var baseLookup = MethodHandles.lookup();
assertThrows(IllegalAccessException.class, () -> MethodHandles.privateLookupIn(clazz, baseLookup));
// thisModule reads the unnamed module
thisModule.addReads(clazz.getModule());
Lookup lookup = MethodHandles.privateLookupIn(clazz, baseLookup);
assertSame(clazz, lookup.lookupClass());
assertEquals(PRIVATE, (lookup.lookupModes() & PRIVATE));
assertEquals(0, (lookup.lookupModes() & MODULE));
}
// test does not read m2, m2 opens p2 to test
@Test
public void testCallerDoesNotRead() throws Throwable {
// m2/p2.Type
Class<?> clazz = Class.forName("p2.Type");
assertEquals("m2", clazz.getModule().getName());
Module thisModule = getClass().getModule();
Module m2 = clazz.getModule();
assertFalse(thisModule.canRead(m2));
assertTrue(m2.isOpen("p2", thisModule));
assertThrows(IllegalAccessException.class, () ->
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()));
}
// test reads m3, m3 does not open p3 to test
@Test
public void testNotOpenToCaller() throws Throwable {
// m3/p2.Type
Class<?> clazz = Class.forName("p3.Type");
assertEquals("m3", clazz.getModule().getName());
Module thisModule = getClass().getModule();
Module m3 = clazz.getModule();
thisModule.addReads(clazz.getModule());
assertFalse(m3.isOpen("p3", thisModule));
assertThrows(IllegalAccessException.class, () ->
MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()));
}
// Invoke MethodHandles.privateLookupIn with a primitive class
@Test
public void testPrimitiveClassAsTargetClass() throws Exception {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.privateLookupIn(int.class, MethodHandles.lookup()));
}
// Invoke MethodHandles.privateLookupIn with an array class
@Test
public void testArrayClassAsTargetClass() throws Exception {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.privateLookupIn(PrivateLookupInTests[].class, MethodHandles.lookup()));
}
// Invoke MethodHandles.privateLookupIn with a primitive array class
@Test
public void testPrimitiveArrayClassAsTargetClass() throws Exception {
assertThrows(IllegalArgumentException.class, () -> MethodHandles.privateLookupIn(int[].class, MethodHandles.lookup()));
}
// Invoke MethodHandles.privateLookupIn with null
@Test
public void testNullTargetClass() throws Exception {
assertThrows(NullPointerException.class, () -> MethodHandles.privateLookupIn(null, MethodHandles.lookup()));
}
// Invoke MethodHandles.privateLookupIn with null
@Test
public void testNullCaller() throws Exception {
assertThrows(NullPointerException.class, () -> MethodHandles.privateLookupIn(getClass(), null));
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p.internal;
class NonPublicType {
private static final Object obj = new Object();
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p.internal;
public class PublicType {
}

View file

@ -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.
*/
/**
* @test
* @bug 8244090
* @build m1/* m2/*
* @run main/othervm m1/p.Main
* @summary Tests public lookups produced from MethodHandles.publicLookup()::in
*/

View file

@ -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.
*/
module m1 {
requires m2;
exports p;
}

View file

@ -0,0 +1,107 @@
/*
* 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.
*/
package p;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.Arrays;
import static java.lang.invoke.MethodHandles.Lookup.*;
public class Main {
/*
* Each Test object loads q.EndPoint and q.T with a custom loader.
* These types are used to look up a method handle "EndPoint::test(T)".
*/
static class Test {
final ClassLoader loader;
final Class<?> target;
final Class<?> param;
Test(String name) throws Exception {
URL url = Paths.get(System.getProperty("test.classes"), "modules", "m2")
.toUri().toURL();
this.loader = new URLClassLoader(name, new URL[]{url}, null);
this.target = Class.forName("q.EndPoint", true, loader);
this.param = Class.forName("q.T", true, loader);
assertTrue(target != q.EndPoint.class);
assertTrue(param != q.T.class);
}
/*
*
*/
public void verifyAccess(Lookup... publicLookups) throws Throwable {
System.err.println(loader.getName() + ": verify access for " + Arrays.toString(publicLookups));
for (Lookup lookup : publicLookups) {
assertTrue((lookup.lookupModes() & UNCONDITIONAL) == UNCONDITIONAL);
assertTrue((lookup.lookupModes() & PUBLIC) == 0);
MethodHandle mh = lookup.findVirtual(target, "test", MethodType.methodType(void.class, param));
mh.invoke(target.newInstance(), param.newInstance());
checkTypeConsistency(mh);
}
}
}
/*
* Verify that publicLookup can teleport to:
* 1) q.EndPoint defined in m2
* 2) q.EndPoint defined by a custom loader CL1 in one unnamed module
* 3) q.EndPoint defined by a custom loader CL2 in another unnamed module
*
* All the resulting Lookup objects are public lookups and can find
* any public accessible member.
*/
public static void main(String... args) throws Throwable {
Test test1 = new Test("CL1");
Test test2 = new Test("CL2");
Lookup lookup1 = MethodHandles.publicLookup();
Lookup lookup2 = MethodHandles.publicLookup().in(test1.target);
Lookup lookup3 = MethodHandles.publicLookup().in(test2.target);
assertTrue(lookup2.lookupClass().getClassLoader() != lookup3.lookupClass().getClassLoader());
test1.verifyAccess(lookup1, lookup2, lookup3);
test2.verifyAccess(lookup1, lookup2, lookup3);
}
static void checkTypeConsistency(MethodHandle mh) throws Throwable {
try {
mh.invoke(new q.EndPoint(), new q.T());
throw new Error("expect fail to invoke due to type inconsistency");
} catch (ClassCastException e) {
assertTrue(e.getMessage().startsWith("Cannot cast q."));
}
}
static void assertTrue(boolean v) {
if (!v) {
throw new AssertionError("unexpected result");
}
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
module m2 {
exports q;
}

View file

@ -0,0 +1,28 @@
/*
* 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.
*/
package q;
public class EndPoint {
public void test(T t) {
}
}

Some files were not shown because too many files have changed in this diff Show more