undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
535
test/jdk/java/lang/invoke/MethodHandles/CatchExceptionTest.java
Normal file
535
test/jdk/java/lang/invoke/MethodHandles/CatchExceptionTest.java
Normal 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>> {
|
||||
}
|
||||
}
|
||||
168
test/jdk/java/lang/invoke/MethodHandles/TestCatchException.java
Normal file
168
test/jdk/java/lang/invoke/MethodHandles/TestCatchException.java
Normal 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");
|
||||
}
|
||||
}
|
||||
68
test/jdk/java/lang/invoke/MethodHandles/TestDropReturn.java
Normal file
68
test/jdk/java/lang/invoke/MethodHandles/TestDropReturn.java
Normal 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" },
|
||||
};
|
||||
}
|
||||
}
|
||||
234
test/jdk/java/lang/invoke/MethodHandles/TestTableSwitch.java
Normal file
234
test/jdk/java/lang/invoke/MethodHandles/TestTableSwitch.java
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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 { }
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
*/
|
||||
package q;
|
||||
|
||||
public class T {
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue