# HG changeset patch # User attila # Date 1597953372 -7200 # Thu Aug 20 21:56:12 2020 +0200 # Node ID e171900ad9fa369de6ba51a78e6aef6178bf998a # Parent eba0f976c4684e71a3320e6fc8ae3a2eab5a29f0 8252124: Restore Dynalink tests diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -295,6 +295,7 @@ javax/smartcardio \ javax/xml \ -javax/xml/crypto \ + jdk/dynalink \ jdk/internal/jline \ com/sun/jndi \ lib/testlibrary diff --git a/test/jdk/jdk/dynalink/BeanLinkerTest.java b/test/jdk/jdk/dynalink/BeanLinkerTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/BeanLinkerTest.java @@ -0,0 +1,631 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 static jdk.dynalink.StandardNamespace.ELEMENT; +import static jdk.dynalink.StandardNamespace.METHOD; +import static jdk.dynalink.StandardNamespace.PROPERTY; +import static jdk.dynalink.StandardOperation.CALL; +import static jdk.dynalink.StandardOperation.GET; +import static jdk.dynalink.StandardOperation.NEW; +import static jdk.dynalink.StandardOperation.REMOVE; +import static jdk.dynalink.StandardOperation.SET; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.DynamicLinker; +import jdk.dynalink.DynamicLinkerFactory; +import jdk.dynalink.NamedOperation; +import jdk.dynalink.NoSuchDynamicMethodException; +import jdk.dynalink.Operation; +import jdk.dynalink.beans.BeansLinker; +import jdk.dynalink.beans.StaticClass; +import jdk.dynalink.support.SimpleRelinkableCallSite; +import org.testng.Assert; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * @test + * @run testng/othervm/java.security.policy=untrusted.security.policy BeanLinkerTest + */ +public class BeanLinkerTest { + + private DynamicLinker linker; + private static final MethodHandles.Lookup MY_LOOKUP = MethodHandles.lookup(); + + @SuppressWarnings("unused") + @DataProvider + private static Object[][] flags() { + return new Object[][]{ + {Boolean.FALSE}, + {Boolean.TRUE} + }; + } + + // helpers to create callsite objects + private CallSite createCallSite(final boolean publicLookup, final Operation op, final MethodType mt) { + return linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + publicLookup ? MethodHandles.publicLookup() : MY_LOOKUP, op, mt))); + } + + private CallSite createCallSite(final boolean publicLookup, final Operation op, final Object name, final MethodType mt) { + return createCallSite(publicLookup, op.named(name), mt); + } + + private CallSite createGetMethodCallSite(final boolean publicLookup, final String name) { + return createCallSite(publicLookup, GET_METHOD, name, MethodType.methodType(Object.class, Object.class)); + } + + private static final MethodHandle throwArrayIndexOutOfBounds = findThrower("throwArrayIndexOutOfBounds"); + private static final MethodHandle throwIndexOutOfBounds = findThrower("throwIndexOutOfBounds"); + + private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY); + private static final Operation GET_ELEMENT = GET.withNamespace(ELEMENT); + private static final Operation GET_METHOD = GET.withNamespace(METHOD); + private static final Operation SET_ELEMENT = SET.withNamespace(ELEMENT); + private static final Operation REMOVE_ELEMENT = REMOVE.withNamespace(ELEMENT); + + private static MethodHandle findThrower(final String name) { + try { + return MethodHandles.lookup().findStatic(BeanLinkerTest.class, name, + MethodType.methodType(Object.class, Object.class, Object.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + Assert.fail("Unexpected exception", e); + return null; + } + } + + private static Object throwArrayIndexOutOfBounds(final Object receiver, final Object index) { + throw new ArrayIndexOutOfBoundsException(String.valueOf(index)); + } + + private static Object throwIndexOutOfBounds(final Object receiver, final Object index) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + + @BeforeTest + public void initLinker() { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + factory.setFallbackLinkers(new BeansLinker((req, services) -> { + // This is a MissingMemberHandlerFactory that creates a missing + // member handler for element getters and setters that throw an + // ArrayIndexOutOfBoundsException when applied to an array and an + // IndexOutOfBoundsException when applied to a list. + + final CallSiteDescriptor desc = req.getCallSiteDescriptor(); + final Operation op = desc.getOperation(); + final Operation baseOp = NamedOperation.getBaseOperation(op); + if (baseOp != GET_ELEMENT && baseOp != SET_ELEMENT && baseOp != REMOVE_ELEMENT) { + // We only handle GET_ELEMENT, SET_ELEMENT and REMOVE_ELEMENT. + return null; + } + + final Object receiver = req.getReceiver(); + Assert.assertNotNull(receiver); + + final Class clazz = receiver.getClass(); + final MethodHandle throwerHandle; + if (clazz.isArray()) { + throwerHandle = throwArrayIndexOutOfBounds; + } else if (List.class.isAssignableFrom(clazz)) { + throwerHandle = throwIndexOutOfBounds; + } else if (Map.class.isAssignableFrom(clazz)) { + return null; + } else { + Assert.fail("Unexpected receiver type " + clazz.getName()); + return null; + } + + final Object name = NamedOperation.getName(op); + final MethodHandle nameBoundHandle; + if (name == null) { + nameBoundHandle = throwerHandle; + } else { + // If the operation is for a fixed index, bind it + nameBoundHandle = MethodHandles.insertArguments(throwerHandle, 1, name); + } + + final MethodType callSiteType = desc.getMethodType(); + final MethodHandle arityMatchedHandle; + if (baseOp == SET_ELEMENT) { + // Drop "value" parameter for a setter + final int handleArity = nameBoundHandle.type().parameterCount(); + arityMatchedHandle = MethodHandles.dropArguments(nameBoundHandle, + handleArity, callSiteType.parameterType(handleArity)); + } else { + arityMatchedHandle = nameBoundHandle; + } + + return arityMatchedHandle.asType(callSiteType); + })); + this.linker = factory.createLinker(); + } + + @AfterTest + public void afterTest() { + this.linker = null; + } + + @Test(dataProvider = "flags") + public void getPropertyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt); + Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class); + Assert.assertEquals(cs.getTarget().invoke(new Date(), "class"), Date.class); + } + + @Test(dataProvider = "flags") + public void getPropertyNegativeTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt); + Assert.assertNull(cs.getTarget().invoke(new Object(), "DOES_NOT_EXIST")); + } + + @Test(dataProvider = "flags") + public void getPropertyTest2(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "class", mt); + Assert.assertEquals(cs.getTarget().invoke(new Object()), Object.class); + Assert.assertEquals(cs.getTarget().invoke(new Date()), Date.class); + } + + @Test(dataProvider = "flags") + public void getPropertyNegativeTest2(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "DOES_NOT_EXIST", mt); + + try { + cs.getTarget().invoke(new Object()); + throw new RuntimeException("Expected NoSuchDynamicMethodException"); + } catch (final Throwable th) { + Assert.assertTrue(th instanceof NoSuchDynamicMethodException); + } + } + + @Test(dataProvider = "flags") + public void getLengthPropertyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(int.class, Object.class, String.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt); + + Assert.assertEquals((int) cs.getTarget().invoke(new int[10], "length"), 10); + Assert.assertEquals((int) cs.getTarget().invoke(new String[33], "length"), 33); + } + + @Test(dataProvider = "flags") + public void getElementTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(int.class, Object.class, int.class); + final CallSite cs = createCallSite(publicLookup, GET_ELEMENT, mt); + + final int[] arr = {23, 42}; + Assert.assertEquals((int) cs.getTarget().invoke(arr, 0), 23); + Assert.assertEquals((int) cs.getTarget().invoke(arr, 1), 42); + try { + final int x = (int) cs.getTarget().invoke(arr, -1); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + final int x = (int) cs.getTarget().invoke(arr, arr.length); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + Assert.assertEquals((int) cs.getTarget().invoke(list, 0), (int) list.get(0)); + Assert.assertEquals((int) cs.getTarget().invoke(list, 1), (int) list.get(1)); + Assert.assertEquals((int) cs.getTarget().invoke(list, 2), (int) list.get(2)); + try { + cs.getTarget().invoke(list, -1); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + cs.getTarget().invoke(list, list.size()); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + private Object invokeWithFixedKey(boolean publicLookup, Operation op, Object name, MethodType mt, Object... args) throws Throwable { + return createCallSite(publicLookup, op.named(name), mt).getTarget().invokeWithArguments(args); + } + + @Test(dataProvider = "flags") + public void getElementWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(int.class, Object.class); + + final int[] arr = {23, 42}; + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 0, mt, arr), 23); + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 1, mt, arr), 42); + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, arr); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, arr.length, mt, arr); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + for (int i = 0; i < 3; ++i) { + Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, i, mt, list), (int) list.get(i)); + } + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, list); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, GET_ELEMENT, list.size(), mt, list); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + @Test(dataProvider = "flags") + public void setElementTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class, int.class, int.class); + final CallSite cs = createCallSite(publicLookup, SET_ELEMENT, mt); + + final int[] arr = {23, 42}; + cs.getTarget().invoke(arr, 0, 0); + Assert.assertEquals(arr[0], 0); + cs.getTarget().invoke(arr, 1, -5); + Assert.assertEquals(arr[1], -5); + + try { + cs.getTarget().invoke(arr, -1, 12); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + cs.getTarget().invoke(arr, arr.length, 20); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + + cs.getTarget().invoke(list, 0, -list.get(0)); + Assert.assertEquals((int) list.get(0), -23); + cs.getTarget().invoke(list, 1, -430); + Assert.assertEquals((int) list.get(1), -430); + cs.getTarget().invoke(list, 2, 4354); + Assert.assertEquals((int) list.get(2), 4354); + try { + cs.getTarget().invoke(list, -1, 343); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + cs.getTarget().invoke(list, list.size(), 43543); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + @Test(dataProvider = "flags") + public void setElementWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class, int.class); + + final int[] arr = {23, 42}; + invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, arr, 0); + Assert.assertEquals(arr[0], 0); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, arr, -5); + Assert.assertEquals(arr[1], -5); + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, arr, 12); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, arr.length, mt, arr, 20); + throw new RuntimeException("expected ArrayIndexOutOfBoundsException"); + } catch (final ArrayIndexOutOfBoundsException ex) { + } + + final List list = new ArrayList<>(); + list.add(23); + list.add(430); + list.add(-4354); + + invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, list, -list.get(0)); + Assert.assertEquals((int) list.get(0), -23); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, list, -430); + Assert.assertEquals((int) list.get(1), -430); + invokeWithFixedKey(publicLookup, SET_ELEMENT, 2, mt, list, 4354); + Assert.assertEquals((int) list.get(2), 4354); + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, list, 343); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + invokeWithFixedKey(publicLookup, SET_ELEMENT, list.size(), mt, list, 43543); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + @Test(dataProvider = "flags") + public void newObjectTest(final boolean publicLookup) { + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final CallSite cs = createCallSite(publicLookup, NEW, mt); + + Object obj = null; + try { + obj = cs.getTarget().invoke(StaticClass.forClass(Date.class)); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertTrue(obj instanceof Date); + } + + @Test(dataProvider = "flags") + public void staticPropertyTest(final boolean publicLookup) { + final MethodType mt = MethodType.methodType(Object.class, Class.class); + final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "static", mt); + + Object obj = null; + try { + obj = cs.getTarget().invoke(Object.class); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertTrue(obj instanceof StaticClass); + Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Object.class); + + try { + obj = cs.getTarget().invoke(Date.class); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertTrue(obj instanceof StaticClass); + Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Date.class); + + try { + obj = cs.getTarget().invoke(Object[].class); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertTrue(obj instanceof StaticClass); + Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Object[].class); + } + + @Test(dataProvider = "flags") + public void instanceMethodCallTest(final boolean publicLookup) { + final CallSite cs = createGetMethodCallSite(publicLookup, "getClass"); + final MethodType mt2 = MethodType.methodType(Class.class, Object.class, Object.class); + final CallSite cs2 = createCallSite(publicLookup, CALL, mt2); + + Object method = null; + try { + method = cs.getTarget().invoke(new Date()); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertNotNull(method); + Assert.assertTrue(BeansLinker.isDynamicMethod(method)); + Class clz = null; + try { + clz = (Class) cs2.getTarget().invoke(method, new Date()); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertEquals(clz, Date.class); + } + + @Test(dataProvider = "flags") + public void staticMethodCallTest(final boolean publicLookup) { + final CallSite cs = createGetMethodCallSite(publicLookup, "getProperty"); + final MethodType mt2 = MethodType.methodType(String.class, Object.class, Object.class, String.class); + final CallSite cs2 = createCallSite(publicLookup, CALL, mt2); + + Object method = null; + try { + method = cs.getTarget().invoke(StaticClass.forClass(System.class)); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + + Assert.assertNotNull(method); + Assert.assertTrue(BeansLinker.isDynamicMethod(method)); + + String str = null; + try { + str = (String) cs2.getTarget().invoke(method, null, "os.name"); + } catch (final Throwable th) { + throw new RuntimeException(th); + } + Assert.assertEquals(str, System.getProperty("os.name")); + } + + // try calling System.getenv and expect security exception + @Test(dataProvider = "flags") + public void systemGetenvTest(final boolean publicLookup) { + final CallSite cs1 = createGetMethodCallSite(publicLookup, "getenv"); + final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(Object.class, Object.class, Object.class)); + + try { + final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class)); + cs2.getTarget().invoke(method, StaticClass.forClass(System.class)); + throw new RuntimeException("should not reach here in any case!"); + } catch (final Throwable th) { + Assert.assertTrue(th instanceof SecurityException); + } + } + + // try getting a specific sensitive System property and expect security exception + @Test(dataProvider = "flags") + public void systemGetPropertyTest(final boolean publicLookup) { + final CallSite cs1 = createGetMethodCallSite(publicLookup, "getProperty"); + final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(String.class, Object.class, Object.class, String.class)); + + try { + final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class)); + cs2.getTarget().invoke(method, StaticClass.forClass(System.class), "java.home"); + throw new RuntimeException("should not reach here in any case!"); + } catch (final Throwable th) { + Assert.assertTrue(th instanceof SecurityException); + } + } + + // check a @CallerSensitive API and expect appropriate access check exception + @Test(dataProvider = "flags") + public void systemLoadLibraryTest(final boolean publicLookup) { + final CallSite cs1 = createGetMethodCallSite(publicLookup, "loadLibrary"); + final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(void.class, Object.class, Object.class, String.class)); + + try { + final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class)); + cs2.getTarget().invoke(method, StaticClass.forClass(System.class), "foo"); + throw new RuntimeException("should not reach here in any case!"); + } catch (final Throwable th) { + if (publicLookup) { + Assert.assertTrue(th instanceof IllegalAccessError); + } else { + Assert.assertTrue(th instanceof AccessControlException, "Expected AccessControlException, got " + th.getClass().getName()); + } + } + } + + @Test(dataProvider = "flags") + public void removeElementFromListTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class, int.class); + final CallSite cs = createCallSite(publicLookup, REMOVE_ELEMENT, mt); + + final List list = new ArrayList<>(List.of(23, 430, -4354)); + + cs.getTarget().invoke(list, 1); + Assert.assertEquals(list, List.of(23, -4354)); + cs.getTarget().invoke(list, 1); + Assert.assertEquals(list, List.of(23)); + cs.getTarget().invoke(list, 0); + Assert.assertEquals(list, List.of()); + try { + cs.getTarget().invoke(list, -1); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + cs.getTarget().invoke(list, list.size()); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + @Test(dataProvider = "flags") + public void removeElementFromListWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class); + + final List list = new ArrayList<>(List.of(23, 430, -4354)); + + createCallSite(publicLookup, REMOVE_ELEMENT.named(1), mt).getTarget().invoke(list); + Assert.assertEquals(list, List.of(23, -4354)); + createCallSite(publicLookup, REMOVE_ELEMENT.named(1), mt).getTarget().invoke(list); + Assert.assertEquals(list, List.of(23)); + createCallSite(publicLookup, REMOVE_ELEMENT.named(0), mt).getTarget().invoke(list); + Assert.assertEquals(list, List.of()); + try { + createCallSite(publicLookup, REMOVE_ELEMENT.named(-1), mt).getTarget().invoke(list); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + + try { + createCallSite(publicLookup, REMOVE_ELEMENT.named(list.size()), mt).getTarget().invoke(list); + throw new RuntimeException("expected IndexOutOfBoundsException"); + } catch (final IndexOutOfBoundsException ex) { + } + } + + @Test(dataProvider = "flags") + public void removeElementFromMapTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class, Object.class); + final CallSite cs = createCallSite(publicLookup, REMOVE_ELEMENT, mt); + + final Map map = new HashMap<>(Map.of("k1", "v1", "k2", "v2", "k3", "v3")); + + cs.getTarget().invoke(map, "k2"); + Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3")); + cs.getTarget().invoke(map, "k4"); + Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3")); + cs.getTarget().invoke(map, "k1"); + Assert.assertEquals(map, Map.of("k3", "v3")); + } + + + @Test(dataProvider = "flags") + public void removeElementFromMapWithFixedKeyTest(final boolean publicLookup) throws Throwable { + final MethodType mt = MethodType.methodType(void.class, Object.class); + + final Map map = new HashMap<>(Map.of("k1", "v1", "k2", "v2", "k3", "v3")); + + createCallSite(publicLookup, REMOVE_ELEMENT.named("k2"), mt).getTarget().invoke(map); + Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3")); + createCallSite(publicLookup, REMOVE_ELEMENT.named("k4"), mt).getTarget().invoke(map); + Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3")); + createCallSite(publicLookup, REMOVE_ELEMENT.named("k1"), mt).getTarget().invoke(map); + Assert.assertEquals(map, Map.of("k3", "v3")); + } +} diff --git a/test/jdk/jdk/dynalink/BeansLinkerTest.java b/test/jdk/jdk/dynalink/BeansLinkerTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/BeansLinkerTest.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 static jdk.dynalink.StandardNamespace.ELEMENT; +import static jdk.dynalink.StandardNamespace.METHOD; +import static jdk.dynalink.StandardNamespace.PROPERTY; +import static jdk.dynalink.StandardOperation.CALL; +import static jdk.dynalink.StandardOperation.GET; +import static jdk.dynalink.StandardOperation.SET; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Stream; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.DynamicLinkerFactory; +import jdk.dynalink.Namespace; +import jdk.dynalink.NamespaceOperation; +import jdk.dynalink.NoSuchDynamicMethodException; +import jdk.dynalink.Operation; +import jdk.dynalink.beans.StaticClass; +import jdk.dynalink.support.SimpleRelinkableCallSite; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @test + * @run testng BeansLinkerTest + */ +public class BeansLinkerTest { + public static class Bean1 { + public final int answer = 42; + + public String getName() { + return "bean1"; + } + + public String someMethod(final String x) { + return x + "-foo"; + } + } + + @Test + public static void testPublicFieldPropertyUnnamedGetter() { + testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals(42, call(op, new Bean1(), "answer"))); + } + + @Test + public static void testPublicFieldPropertyNamedGetter() { + testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals(42, call(op.named("answer"), new Bean1()))); + } + + @Test + public static void testGetterPropertyUnnamedGetter() { + testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals("bean1", call(op, new Bean1(), "name"))); + } + + @Test + public static void testGetterPropertyNamedGetter() { + testGetterPermutations(PROPERTY, (op) -> Assert.assertEquals("bean1", call(op.named("name"), new Bean1()))); + } + + @Test + public static void testMethodUnnamedGetter() { + testGetterPermutations(METHOD, (op) -> Assert.assertEquals("bar-foo", call(call(op, new Bean1(), "someMethod"), new Bean1(), "bar"))); + } + + @Test + public static void testMethodNamedGetter() { + testGetterPermutations(METHOD, (op) -> Assert.assertEquals("bar-foo", call(call(op.named("someMethod"), new Bean1()), new Bean1(), "bar"))); + } + + private static final Map MAP1 = new HashMap<>(); + static { + MAP1.put("foo", "bar"); + } + + @Test + public static void testElementUnnamedGetter() { + testGetterPermutations(ELEMENT, (op) -> Assert.assertEquals("bar", call(op, MAP1, "foo"))); + } + + @Test + public static void testElementNamedGetter() { + testGetterPermutations(ELEMENT, (op) -> Assert.assertEquals("bar", call(op.named("foo"), MAP1))); + } + + public static class Bean2 { + public int answer; + private String name; + + public void setName(final String name) { + this.name = name; + } + } + + @Test + public static void testUnnamedFieldSetter() { + testSetterPermutations(PROPERTY, (op) -> { + final Bean2 bean2 = new Bean2(); + call(op, bean2, "answer", 12); + Assert.assertEquals(bean2.answer, 12); + }); + } + + @Test + public static void testNamedFieldSetter() { + testSetterPermutations(PROPERTY, (op) -> { + final Bean2 bean2 = new Bean2(); + call(op.named("answer"), bean2, 14); + Assert.assertEquals(bean2.answer, 14); + }); + } + + @Test + public static void testUnnamedPropertySetter() { + testSetterPermutations(PROPERTY, (op) -> { + final Bean2 bean2 = new Bean2(); + call(op, bean2, "name", "boo"); + Assert.assertEquals(bean2.name, "boo"); + }); + } + + @Test + public static void testNamedPropertySetter() { + testSetterPermutations(PROPERTY, (op) -> { + final Bean2 bean2 = new Bean2(); + call(op.named("name"), bean2, "blah"); + Assert.assertEquals(bean2.name, "blah"); + }); + } + + private static final Pattern GET_ELEMENT_THEN_PROPERTY_PATTERN = Pattern.compile(".*ELEMENT.*PROPERTY.*"); + + @Test + public static void testUnnamedElementAndPropertyGetter() { + final Map map = new HashMap<>(); + map.put("empty", true); + testGetterPermutations(GET_ELEMENT_THEN_PROPERTY_PATTERN, 4, (op) -> Assert.assertEquals(true, call(op, map, "empty"))); + } + + @Test + public static void testNamedElementAndPropertyGetter() { + final Map map = new HashMap<>(); + map.put("empty", true); + testGetterPermutations(GET_ELEMENT_THEN_PROPERTY_PATTERN, 4, (op) -> Assert.assertEquals(true, call(op.named("empty"), map))); + } + + private static final Pattern GET_PROPERTY_THEN_ELEMENT_PATTERN = Pattern.compile(".*PROPERTY.*ELEMENT.*"); + + @Test + public static void testUnnamedPropertyAndElementGetter() { + final Map map = new HashMap<>(); + map.put("empty", true); + testGetterPermutations(GET_PROPERTY_THEN_ELEMENT_PATTERN, 4, (op) -> Assert.assertEquals(false, call(op, map, "empty"))); + } + + @Test + public static void testNamedPropertyAndElementGetter() { + final Map map = new HashMap<>(); + map.put("empty", true); + testGetterPermutations(GET_PROPERTY_THEN_ELEMENT_PATTERN, 4, (op) -> Assert.assertEquals(false, call(op.named("empty"), map))); + } + + public static class MapWithProperty extends HashMap { + private String name; + + public void setName(final String name) { + this.name = name; + } + } + + @Test + public static void testUnnamedPropertyAndElementSetter() { + final MapWithProperty map = new MapWithProperty(); + map.put("name", "element"); + + call(SET.withNamespaces(PROPERTY, ELEMENT), map, "name", "property"); + Assert.assertEquals("property", map.name); + Assert.assertEquals("element", map.get("name")); + + call(SET.withNamespaces(ELEMENT, PROPERTY), map, "name", "element2"); + Assert.assertEquals("property", map.name); + Assert.assertEquals("element2", map.get("name")); + } + + @Test + public static void testMissingMembersAtLinkTime() { + testPermutations(GETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(op.named("foo"), new Object()))); + testPermutations(SETTER_PERMUTATIONS, (op) -> expectNoSuchDynamicMethodException(()-> call(op.named("foo"), new Object(), "newValue"))); + } + + @Test + public static void testMissingMembersAtRunTime() { + call(GET.withNamespace(ELEMENT), new ArrayList<>(), "foo"); + Stream.of(new HashMap(), new ArrayList(), new Object[0]).forEach((receiver) -> { + testPermutations(GETTER_PERMUTATIONS, (op) -> { System.err.println(op + " " + receiver.getClass().getName()); Assert.assertNull(call(op, receiver, "foo"));}); + // No assertion for the setter; we just expect it to silently succeed + testPermutations(SETTER_PERMUTATIONS, (op) -> call(op, receiver, "foo", "newValue")); + }); + } + + public static class A { + public static class Inner {} + } + + public static class B extends A { + public static class Inner {} + } + + @Test + public static void testInnerClassGetter() { + Object inner1 = call(GET.withNamespace(PROPERTY), StaticClass.forClass(A.class), "Inner"); + Assert.assertTrue(inner1 instanceof StaticClass); + Assert.assertEquals(A.Inner.class, ((StaticClass) inner1).getRepresentedClass()); + + Object inner2 = call(GET.withNamespace(PROPERTY), StaticClass.forClass(B.class), "Inner"); + Assert.assertTrue(inner2 instanceof StaticClass); + Assert.assertEquals(B.Inner.class, ((StaticClass) inner2).getRepresentedClass()); + } + + private static void expectNoSuchDynamicMethodException(final Runnable r) { + try { + r.run(); + Assert.fail("Should've thrown NoSuchDynamicMethodException"); + } catch(final NoSuchDynamicMethodException e) { + } + } + + private static final NamespaceOperation[] GETTER_PERMUTATIONS = new NamespaceOperation[] { + GET.withNamespaces(PROPERTY), + GET.withNamespaces(METHOD), + GET.withNamespaces(ELEMENT), + GET.withNamespaces(PROPERTY, ELEMENT), + GET.withNamespaces(PROPERTY, METHOD), + GET.withNamespaces(ELEMENT, PROPERTY), + GET.withNamespaces(ELEMENT, METHOD), + GET.withNamespaces(METHOD, PROPERTY), + GET.withNamespaces(METHOD, ELEMENT), + GET.withNamespaces(PROPERTY, ELEMENT, METHOD), + GET.withNamespaces(PROPERTY, METHOD, ELEMENT), + GET.withNamespaces(ELEMENT, PROPERTY, METHOD), + GET.withNamespaces(ELEMENT, METHOD, PROPERTY), + GET.withNamespaces(METHOD, PROPERTY, ELEMENT), + GET.withNamespaces(METHOD, ELEMENT, PROPERTY) + }; + + private static final NamespaceOperation[] SETTER_PERMUTATIONS = new NamespaceOperation[] { + SET.withNamespaces(PROPERTY), + SET.withNamespaces(ELEMENT), + SET.withNamespaces(PROPERTY, ELEMENT), + SET.withNamespaces(ELEMENT, PROPERTY) + }; + + private static void testPermutations(final NamespaceOperation[] ops, final Operation requiredOp, final Namespace requiredNamespace, final int expectedCount, final Consumer test) { + testPermutationsWithFilter(ops, (op)->NamespaceOperation.contains(op, requiredOp, requiredNamespace), expectedCount, test); + } + + private static void testPermutations(final NamespaceOperation[] ops, final Pattern regex, final int expectedCount, final Consumer test) { + testPermutationsWithFilter(ops, (op)->regex.matcher(op.toString()).matches(), expectedCount, test); + } + + private static void testPermutations(final NamespaceOperation[] ops, final Consumer test) { + testPermutationsWithFilter(ops, (op)->true, ops.length, test); + } + + private static void testPermutationsWithFilter(final NamespaceOperation[] ops, final Predicate filter, final int expectedCount, final Consumer test) { + final int[] counter = new int[1]; + Stream.of(ops).filter(filter).forEach((op)-> { counter[0]++; test.accept(op); }); + Assert.assertEquals(counter[0], expectedCount); + } + + private static void testGetterPermutations(final Namespace requiredNamespace, final Consumer test) { + testPermutations(GETTER_PERMUTATIONS, GET, requiredNamespace, 11, test); + } + + private static void testGetterPermutations(final Pattern regex, final int expectedCount, final Consumer test) { + testPermutations(GETTER_PERMUTATIONS, regex, expectedCount, test); + } + + private static void testSetterPermutations(final Namespace requiredNamespace, final Consumer test) { + testPermutations(SETTER_PERMUTATIONS, SET, requiredNamespace, 3, test); + } + + private static Object call(final Operation op, final Object... args) { + try { + return new DynamicLinkerFactory().createLinker().link( + new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), op, t(args.length)))) + .dynamicInvoker().invokeWithArguments(args); + } catch (final Error|RuntimeException e) { + throw e; + } catch (final Throwable t) { + throw new RuntimeException(t); + } + } + + private static Object call(final Object... args) { + return call(CALL, args); + } + + private static MethodType t(final int argCount) { + return MethodType.methodType(Object.class, Collections.nCopies(argCount, Object.class)); + } +} diff --git a/test/jdk/jdk/dynalink/CallSiteTest.java b/test/jdk/jdk/dynalink/CallSiteTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/CallSiteTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 static jdk.dynalink.StandardNamespace.PROPERTY; +import static jdk.dynalink.StandardOperation.GET; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.ArrayList; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.DynamicLinker; +import jdk.dynalink.DynamicLinkerFactory; +import jdk.dynalink.Operation; +import jdk.dynalink.linker.GuardedInvocation; +import jdk.dynalink.support.SimpleRelinkableCallSite; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @test + * @run testng CallSiteTest + */ +public class CallSiteTest { + private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY); + + @Test + public void testInitialize() { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final boolean[] initializeCalled = { Boolean.FALSE }; + linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), GET_PROPERTY.named("DO_NOT_CARE"), mt)) { + @Override + public void initialize(final MethodHandle relinkAndInvoke) { + initializeCalled[0] = Boolean.TRUE; + super.initialize(relinkAndInvoke); + } + }); + + Assert.assertTrue(initializeCalled[0]); + } + + @Test + public void testRelink() { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final boolean[] relinkCalled = { Boolean.FALSE }; + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), GET_PROPERTY.named("class"), mt)) { + @Override + public void relink(final GuardedInvocation guardedInvocation, final MethodHandle relinkAndInvoke) { + relinkCalled[0] = Boolean.TRUE; + super.relink(guardedInvocation, relinkAndInvoke); + } + }); + + Assert.assertFalse(relinkCalled[0]); + try { + cs.getTarget().invoke(new Object()); + } catch (final Throwable th) {} + + Assert.assertTrue(relinkCalled[0]); + } + + @Test + public void testResetAndRelink() { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + factory.setUnstableRelinkThreshold(1); + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final boolean[] resetAndRelinkCalled = { Boolean.FALSE }; + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), GET_PROPERTY.named("length"), mt)) { + @Override + public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle relinkAndInvoke) { + resetAndRelinkCalled[0] = Boolean.TRUE; + super.resetAndRelink(guardedInvocation, relinkAndInvoke); + } + }); + + Assert.assertFalse(resetAndRelinkCalled[0]); + try { + cs.getTarget().invoke(new Object[] {}); + } catch (final Throwable th) {} + + Assert.assertFalse(resetAndRelinkCalled[0]); + try { + cs.getTarget().invoke(new ArrayList()); + } catch (final Throwable th) {} + + Assert.assertTrue(resetAndRelinkCalled[0]); + } +} diff --git a/test/jdk/jdk/dynalink/CallerSensitiveTest.java b/test/jdk/jdk/dynalink/CallerSensitiveTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/CallerSensitiveTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.dynalink.beans.BeansLinker; +import org.testng.annotations.Test; + +/** + * @test + * @build ClassLoaderAware + * @run testng CallerSensitiveTest + */ +public class CallerSensitiveTest { + @Test + public void testCallerSensitive() { + new BeansLinker().getLinkerForClass(ClassLoaderAware.class); + } +} diff --git a/test/jdk/jdk/dynalink/ClassLoaderAware.java b/test/jdk/jdk/dynalink/ClassLoaderAware.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/ClassLoaderAware.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 interface ClassLoaderAware { + public ClassLoader getContextClassLoader(); + public void checkMemberAccess(Class clazz, int which); +} diff --git a/test/jdk/jdk/dynalink/LinkedCallSiteLocationTest.java b/test/jdk/jdk/dynalink/LinkedCallSiteLocationTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/LinkedCallSiteLocationTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.DynamicLinker; +import jdk.dynalink.DynamicLinkerFactory; +import jdk.dynalink.Operation; +import jdk.dynalink.StandardNamespace; +import jdk.dynalink.StandardOperation; +import jdk.dynalink.linker.GuardingDynamicLinker; +import jdk.dynalink.support.SimpleRelinkableCallSite; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @test + * @run testng LinkedCallSiteLocationTest + */ +public class LinkedCallSiteLocationTest { + private static final Operation GET_METHOD = StandardOperation.GET.withNamespace(StandardNamespace.METHOD); + @Test + public void testLinkedCallSiteLocation() throws Throwable { + final StackTraceElement[] lastLinked = new StackTraceElement[1]; + + final GuardingDynamicLinker testLinker = + (r, s) -> { lastLinked[0] = DynamicLinker.getLinkedCallSiteLocation(); return null; }; + + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + factory.setPrioritizedLinker(testLinker); + final DynamicLinker linker = factory.createLinker(); + final SimpleRelinkableCallSite callSite = new SimpleRelinkableCallSite( + new CallSiteDescriptor( + MethodHandles.lookup(), + GET_METHOD.named("foo"), + MethodType.methodType(void.class, Object.class))); + linker.link(callSite); + + // Test initial linking + callSite.dynamicInvoker().invoke(new TestClass1()); final int l1 = getLineNumber(); + assertLocation(lastLinked[0], l1); + + // Test relinking + callSite.dynamicInvoker().invoke(new TestClass2()); final int l2 = getLineNumber(); + assertLocation(lastLinked[0], l2); + } + + private void assertLocation(final StackTraceElement frame, final int lineNumber) { + Assert.assertNotNull(frame); + Assert.assertEquals(frame.getLineNumber(), lineNumber); + Assert.assertEquals(frame.getClassName(), this.getClass().getName()); + } + + private static int getLineNumber() { + return StackWalker.getInstance().walk(s -> s.skip(1).findFirst().get().getLineNumber()); + } + + public static class TestClass1 { + public void foo() { + } + } + + public static class TestClass2 { + public void foo() { + } + } +} diff --git a/test/jdk/jdk/dynalink/LookupTest.java b/test/jdk/jdk/dynalink/LookupTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/LookupTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import jdk.dynalink.linker.support.Lookup; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * @test + * @run testng LookupTest + */ +public class LookupTest { + private static final MethodHandles.Lookup MY_LOOKUP = MethodHandles.lookup(); + + private static MethodHandles.Lookup getLookup(final boolean publicLookup) { + return publicLookup? MethodHandles.publicLookup() : MY_LOOKUP; + } + + // test constructors, methods used for lookup + @SuppressWarnings("unused") + public LookupTest() {} + + @SuppressWarnings("unused") + private LookupTest(final int unused) {} + + @SuppressWarnings("unused") + private void privateFunc() {} + + @SuppressWarnings("unused") + protected void protectedFunc() {} + + @SuppressWarnings("unused") + private static void privateStaticFunc() {} + + @SuppressWarnings("unused") + private final int myIntField = 0; + + @SuppressWarnings("unused") + @DataProvider + private static Object[][] flags() { + return new Object[][]{ + {Boolean.FALSE}, + {Boolean.TRUE} + }; + } + + @Test(dataProvider = "flags") + public void unreflectTest(final boolean publicLookup) throws NoSuchMethodException { + final MethodHandle mh = Lookup.unreflect(getLookup(publicLookup), LookupTest.class.getMethod("unreflectTest", Boolean.TYPE)); + Assert.assertNotNull(mh); + } + + @Test + public void unreflectTest2() throws NoSuchMethodException { + final MethodHandle mh = Lookup.PUBLIC.unreflect(LookupTest.class.getMethod("unreflectTest", Boolean.TYPE)); + Assert.assertNotNull(mh); + } + + @Test(dataProvider = "flags") + public void unreflectNegativeTest(final boolean publicLookup) throws NoSuchMethodException { + try { + final MethodHandle mh = Lookup.unreflect(getLookup(publicLookup), + LookupTest.class.getDeclaredMethod("privateFunc")); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void unreflectNegativeTest2() throws NoSuchMethodException { + try { + Lookup.PUBLIC.unreflect(LookupTest.class.getDeclaredMethod("privateFunc")); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void unreflectConstructorTest(final boolean publicLookup) throws NoSuchMethodException { + final MethodHandle mh = Lookup.unreflectConstructor(getLookup(publicLookup), LookupTest.class.getConstructor()); + Assert.assertNotNull(mh); + } + + @Test + public void unreflectConstructorTest2() throws NoSuchMethodException { + final MethodHandle mh = Lookup.PUBLIC.unreflectConstructor(LookupTest.class.getConstructor()); + Assert.assertNotNull(mh); + } + + @Test(dataProvider = "flags") + public void unreflectConstructorNegativeTest(final boolean publicLookup) throws NoSuchMethodException { + try { + final MethodHandle mh = Lookup.unreflectConstructor(getLookup(publicLookup), + LookupTest.class.getDeclaredConstructor(Integer.TYPE)); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void unreflectConstructorNegativeTest2() throws NoSuchMethodException { + try { + Lookup.PUBLIC.unreflectConstructor( + LookupTest.class.getDeclaredConstructor(Integer.TYPE)); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void findOwnStaticTest(final boolean publicLookup) { + try { + final MethodHandle mh = Lookup.findOwnStatic(getLookup(publicLookup), "getLookup", + MethodHandles.Lookup.class, Boolean.TYPE); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void findOwnStaticTest2() { + try { + Lookup.PUBLIC.findStatic(LookupTest.class, "getLookup", + MethodType.methodType(MethodHandles.Lookup.class, Boolean.TYPE)); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void findOwnSepcialTest(final boolean publicLookup) { + try { + final MethodHandle mh = Lookup.findOwnSpecial(getLookup(publicLookup), "privateFunc", Void.TYPE); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void findOwnSepcialTest2() { + try { + Lookup.PUBLIC.findOwnSpecial("privateFunc", Void.TYPE); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void findGetterTest(final boolean publicLookup) { + try { + final MethodHandle mh = new Lookup(getLookup(publicLookup)).findGetter(LookupTest.class, "myIntField", Integer.TYPE); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void findGetterTest2() { + try { + Lookup.PUBLIC.findGetter(LookupTest.class, "myIntField", Integer.TYPE); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void findVirtualTest(final boolean publicLookup) { + try { + final MethodHandle mh = new Lookup(getLookup(publicLookup)).findVirtual(LookupTest.class, "protectedFunc", + MethodType.methodType(Void.TYPE)); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void findVirtualTest2() { + try { + Lookup.PUBLIC.findVirtual(LookupTest.class, "protectedFunc", + MethodType.methodType(Void.TYPE)); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test(dataProvider = "flags") + public void findStaticTest(final boolean publicLookup) { + try { + final MethodHandle mh = new Lookup(getLookup(publicLookup)).findStatic(LookupTest.class, "privateStaticFunc", + MethodType.methodType(Void.TYPE)); + if (publicLookup) { + throw new RuntimeException("should have thrown Error"); + } + Assert.assertNotNull(mh); + } catch (final Error err) { + Assert.assertTrue(publicLookup); + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } + + @Test + public void findStaticTest2() { + try { + Lookup.PUBLIC.findStatic(LookupTest.class, "privateStaticFunc", + MethodType.methodType(Void.TYPE)); + throw new RuntimeException("should have thrown Error"); + } catch (final Error err) { + Assert.assertTrue(err instanceof NoSuchMethodError || err instanceof IllegalAccessError); + } + } +} diff --git a/test/jdk/jdk/dynalink/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter b/test/jdk/jdk/dynalink/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter @@ -0,0 +1,1 @@ +TestGuardingDynamicLinkerExporter diff --git a/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java b/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.List; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.linker.GuardingDynamicLinker; +import jdk.dynalink.linker.GuardingDynamicLinkerExporter; +import jdk.dynalink.linker.LinkRequest; +import jdk.dynalink.linker.LinkerServices; + +/** + * A trusted linker exporter (build file gives appropriate permission to the jar containing this class!). + */ +public final class TestGuardingDynamicLinkerExporter extends GuardingDynamicLinkerExporter { + + private static final ThreadLocal lastDescriptor = new ThreadLocal<>(); + private static boolean enabled = false; + + public static void enable() { + reset(true); + } + + public static void disable() { + reset(false); + } + public static boolean isLastCallSiteDescriptor(final CallSiteDescriptor desc) { + return lastDescriptor.get() == desc; + } + + private static void reset(final boolean enable) { + lastDescriptor.set(null); + enabled = enable; + } + + @Override + public List get() { + return List.of(((final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + if (enabled) { + lastDescriptor.set(linkRequest.getCallSiteDescriptor()); + } + return null; + })); + } +} diff --git a/test/jdk/jdk/dynalink/TrustedDynamicLinkerFactoryTest.java b/test/jdk/jdk/dynalink/TrustedDynamicLinkerFactoryTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/TrustedDynamicLinkerFactoryTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 static jdk.dynalink.StandardNamespace.PROPERTY; +import static jdk.dynalink.StandardOperation.GET; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import jdk.dynalink.CallSiteDescriptor; +import jdk.dynalink.DynamicLinker; +import jdk.dynalink.DynamicLinkerFactory; +import jdk.dynalink.NoSuchDynamicMethodException; +import jdk.dynalink.Operation; +import jdk.dynalink.StandardNamespace; +import jdk.dynalink.StandardOperation; +import jdk.dynalink.beans.StaticClass; +import jdk.dynalink.linker.GuardedInvocation; +import jdk.dynalink.linker.LinkRequest; +import jdk.dynalink.linker.LinkerServices; +import jdk.dynalink.support.SimpleRelinkableCallSite; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @test + * @build TestGuardingDynamicLinkerExporter + * @run testng/othervm/java.security.policy=trusted.security.policy TrustedDynamicLinkerFactoryTest + */ +public class TrustedDynamicLinkerFactoryTest { + + private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY); + + private static DynamicLinkerFactory newDynamicLinkerFactory(final boolean resetClassLoader) { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + if (resetClassLoader) { + factory.setClassLoader(null); + } + return factory; + } + + @Test + public void callSiteCreationTest() { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final DynamicLinker linker = factory.createLinker(); + final StandardOperation[] operations = StandardOperation.values(); + final MethodType mt = MethodType.methodType(Object.class, Object.class); + for (final Operation op : operations) { + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), op, mt))); + Assert.assertNotNull(cs); + Assert.assertEquals(cs.type(), mt); + Assert.assertNotNull(cs.getTarget()); + } + } + + @Test + public void fallbackLinkerTest() { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final Operation myOperation = new Operation() { + }; + final boolean[] reachedFallback = { false }; + factory.setFallbackLinkers((final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); + reachedFallback[0] = true; + return null; + }); + + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), myOperation, mt))); + + // linking the call site initially does not invoke the linkers! + Assert.assertFalse(reachedFallback[0]); + try { + cs.getTarget().invoke(); + } catch (final NoSuchDynamicMethodException nsdm) { + // we do expect NoSuchDynamicMethod! + // because our dummy fallback linker returns null! + } catch (final Throwable th) { + throw new RuntimeException("should not reach here with: " + th); + } + + // check that the control reached fallback linker! + Assert.assertTrue(reachedFallback[0]); + } + + @Test + public void priorityLinkerTest() { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final Operation myOperation = new Operation() { + }; + final boolean[] reachedProrityLinker = { false }; + factory.setPrioritizedLinker((final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); + reachedProrityLinker[0] = true; + return null; + }); + + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), myOperation, mt))); + + // linking the call site initially does not invoke the linkers! + Assert.assertFalse(reachedProrityLinker[0]); + try { + cs.getTarget().invoke(); + } catch (final NoSuchDynamicMethodException nsdm) { + // we do expect NoSuchDynamicMethod! + // because our dummy priority linker returns null! + } catch (final Throwable th) { + throw new RuntimeException("should not reach here with: " + th); + } + + // check that the control reached fallback linker! + Assert.assertTrue(reachedProrityLinker[0]); + } + + @Test + public void priorityAndFallbackLinkerTest() { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final Operation myOperation = new Operation() { + }; + final int[] linkerReachCounter = { 0 }; + factory.setPrioritizedLinker((final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); + linkerReachCounter[0]++; + return null; + }); + factory.setFallbackLinkers((final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); + Assert.assertEquals(linkerReachCounter[0], 1); + linkerReachCounter[0]++; + return null; + }); + + final DynamicLinker linker = factory.createLinker(); + final MethodType mt = MethodType.methodType(Object.class); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), myOperation, mt))); + + // linking the call site initially does not invoke the linkers! + Assert.assertEquals(linkerReachCounter[0], 0); + + try { + cs.getTarget().invoke(); + } catch (final NoSuchDynamicMethodException nsdm) { + // we do expect NoSuchDynamicMethod! + } catch (final Throwable th) { + throw new RuntimeException("should not reach here with: " + th); + } + + Assert.assertEquals(linkerReachCounter[0], 2); + } + + @Test + public void prelinkTransformerTest() throws Throwable { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final boolean[] reachedPrelinkTransformer = { false }; + + factory.setPrelinkTransformer((final GuardedInvocation inv, final LinkRequest linkRequest, final LinkerServices linkerServices) -> { + reachedPrelinkTransformer[0] = true; + // just identity transformer! + return inv; + }); + + final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); + final DynamicLinker linker = factory.createLinker(); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), GET_PROPERTY, mt))); + Assert.assertFalse(reachedPrelinkTransformer[0]); + Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class); + Assert.assertTrue(reachedPrelinkTransformer[0]); + } + + @Test + public void internalObjectsFilterTest() throws Throwable { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); + final boolean[] reachedInternalObjectsFilter = { false }; + + factory.setInternalObjectsFilter((final MethodHandle mh) -> { + reachedInternalObjectsFilter[0] = true; + return mh; + }); + + final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); + final DynamicLinker linker = factory.createLinker(); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( + MethodHandles.publicLookup(), GET_PROPERTY, mt))); + Assert.assertFalse(reachedInternalObjectsFilter[0]); + Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class); + Assert.assertTrue(reachedInternalObjectsFilter[0]); + } + + @Test + public void autoLoadedLinkerTest() { + testAutoLoadedLinkerInvoked(new Object(), "toString"); + } + + @Test + public void autoLoadedLinkerSeesStaticMethod() { + testAutoLoadedLinkerInvoked(StaticClass.forClass(System.class), "currentTimeMillis"); + } + + private static void testAutoLoadedLinkerInvoked(final Object target, final String methodName) { + final DynamicLinkerFactory factory = newDynamicLinkerFactory(false); + final DynamicLinker linker = factory.createLinker(); + + final MethodType mt = MethodType.methodType(Object.class, Object.class); + final CallSiteDescriptor testDescriptor = new CallSiteDescriptor(MethodHandles.publicLookup(), + GET.withNamespace(StandardNamespace.METHOD).named(methodName), mt); + final CallSite cs = linker.link(new SimpleRelinkableCallSite(testDescriptor)); + + TestGuardingDynamicLinkerExporter.enable(); + try { + cs.getTarget().invoke(target); + // The linker was loaded and it observed our invocation + Assert.assertTrue(TestGuardingDynamicLinkerExporter.isLastCallSiteDescriptor(testDescriptor)); + } catch (final Throwable th) { + throw new RuntimeException(th); + } finally { + TestGuardingDynamicLinkerExporter.disable(); + } + + } +} diff --git a/test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java b/test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.util.List; +import java.util.ServiceConfigurationError; +import jdk.dynalink.DynamicLinkerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * @test + * @build TestGuardingDynamicLinkerExporter + * @run testng/othervm/java.security.policy=untrusted.security.policy UntrustedDynamicLinkerFactoryTest + */ +public class UntrustedDynamicLinkerFactoryTest { + @Test + public void autoLoadedLinkerNegativeTest() { + final DynamicLinkerFactory factory = new DynamicLinkerFactory(); + factory.createLinker(); + // expect one error as we have one untrusted linker exporter in META-INF/services + final List autoLoadingErrors = factory.getAutoLoadingErrors(); + // single error ... + Assert.assertEquals(autoLoadingErrors.size(), 1); + autoLoadingErrors.get(0).printStackTrace(); + final Throwable cause = autoLoadingErrors.get(0).getCause(); + // .. due to permission check.. + Assert.assertTrue(cause.toString().contains("dynalink.exportLinkersAutomatically")); + } +} diff --git a/test/jdk/jdk/dynalink/trusted.security.policy b/test/jdk/jdk/dynalink/trusted.security.policy new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/trusted.security.policy @@ -0,0 +1,7 @@ +grant { + permission java.io.FilePermission "${user.home}/-", "read"; +}; + +grant codeBase "file:${test.classes}/-" { + permission java.lang.RuntimePermission "dynalink.exportLinkersAutomatically"; +}; diff --git a/test/jdk/jdk/dynalink/untrusted.security.policy b/test/jdk/jdk/dynalink/untrusted.security.policy new file mode 100644 --- /dev/null +++ b/test/jdk/jdk/dynalink/untrusted.security.policy @@ -0,0 +1,3 @@ +grant { + permission java.io.FilePermission "${user.home}/-", "read"; +};