From 97f8261e4190b8edf83c1d8f11ea57f6c8338284 Mon Sep 17 00:00:00 2001
From: Attila Szegedi <attila@openjdk.org>
Date: Sun, 23 Aug 2020 14:58:59 +0200
Subject: [PATCH] 8252124: Restore Dynalink tests

Reviewed-by: sundar
---
 test/jdk/TEST.groups                          |   1 +
 test/jdk/jdk/dynalink/BeanLinkerTest.java     | 631 ++++++++++++++++++
 test/jdk/jdk/dynalink/BeansLinkerTest.java    | 333 +++++++++
 test/jdk/jdk/dynalink/CallSiteTest.java       | 119 ++++
 .../jdk/jdk/dynalink/CallerSensitiveTest.java |  39 ++
 test/jdk/jdk/dynalink/ClassLoaderAware.java   |  29 +
 .../dynalink/LinkedCallSiteLocationTest.java  |  90 +++
 test/jdk/jdk/dynalink/LookupTest.java         | 273 ++++++++
 ...alink.linker.GuardingDynamicLinkerExporter |   1 +
 .../TestGuardingDynamicLinkerExporter.java    |  66 ++
 .../TrustedDynamicLinkerFactoryTest.java      | 250 +++++++
 .../UntrustedDynamicLinkerFactoryTest.java    |  50 ++
 test/jdk/jdk/dynalink/trusted.security.policy |   7 +
 .../jdk/dynalink/untrusted.security.policy    |   3 +
 14 files changed, 1892 insertions(+)
 create mode 100644 test/jdk/jdk/dynalink/BeanLinkerTest.java
 create mode 100644 test/jdk/jdk/dynalink/BeansLinkerTest.java
 create mode 100644 test/jdk/jdk/dynalink/CallSiteTest.java
 create mode 100644 test/jdk/jdk/dynalink/CallerSensitiveTest.java
 create mode 100644 test/jdk/jdk/dynalink/ClassLoaderAware.java
 create mode 100644 test/jdk/jdk/dynalink/LinkedCallSiteLocationTest.java
 create mode 100644 test/jdk/jdk/dynalink/LookupTest.java
 create mode 100644 test/jdk/jdk/dynalink/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter
 create mode 100644 test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java
 create mode 100644 test/jdk/jdk/dynalink/TrustedDynamicLinkerFactoryTest.java
 create mode 100644 test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java
 create mode 100644 test/jdk/jdk/dynalink/trusted.security.policy
 create mode 100644 test/jdk/jdk/dynalink/untrusted.security.policy

diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups
index 132692d34c9..ba3603095a3 100644
--- a/test/jdk/TEST.groups
+++ b/test/jdk/TEST.groups
@@ -295,6 +295,7 @@ jdk_other = \
     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
index 00000000000..fafc1be447f
--- /dev/null
+++ b/test/jdk/jdk/dynalink/BeanLinkerTest.java
@@ -0,0 +1,631 @@
+/*
+ * 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.  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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<String, String> 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<String, String> 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
index 00000000000..474bfa60d19
--- /dev/null
+++ b/test/jdk/jdk/dynalink/BeansLinkerTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.  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<String, String> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> {
+        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<NamespaceOperation> 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<NamespaceOperation> test) {
+        testPermutationsWithFilter(ops, (op)->regex.matcher(op.toString()).matches(), expectedCount, test);
+    }
+
+    private static void testPermutations(final NamespaceOperation[] ops, final Consumer<NamespaceOperation> test) {
+        testPermutationsWithFilter(ops, (op)->true, ops.length, test);
+    }
+
+    private static void testPermutationsWithFilter(final NamespaceOperation[] ops, final Predicate<NamespaceOperation> filter, final int expectedCount, final Consumer<NamespaceOperation> 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<NamespaceOperation> test) {
+        testPermutations(GETTER_PERMUTATIONS, GET, requiredNamespace, 11, test);
+    }
+
+    private static void testGetterPermutations(final Pattern regex, final int expectedCount, final Consumer<NamespaceOperation> test) {
+        testPermutations(GETTER_PERMUTATIONS, regex, expectedCount, test);
+    }
+
+    private static void testSetterPermutations(final Namespace requiredNamespace, final Consumer<NamespaceOperation> 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
index 00000000000..f31adbd5a94
--- /dev/null
+++ b/test/jdk/jdk/dynalink/CallSiteTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.  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<Object>());
+        } 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
index 00000000000..c196abebc04
--- /dev/null
+++ b/test/jdk/jdk/dynalink/CallerSensitiveTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.  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
index 00000000000..d9c65b7c6ee
--- /dev/null
+++ b/test/jdk/jdk/dynalink/ClassLoaderAware.java
@@ -0,0 +1,29 @@
+/*
+ * 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.  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
index 00000000000..a9533dac58d
--- /dev/null
+++ b/test/jdk/jdk/dynalink/LinkedCallSiteLocationTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.  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
index 00000000000..405937ec430
--- /dev/null
+++ b/test/jdk/jdk/dynalink/LookupTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.  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
index 00000000000..c969b0f3429
--- /dev/null
+++ b/test/jdk/jdk/dynalink/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter
@@ -0,0 +1 @@
+TestGuardingDynamicLinkerExporter
diff --git a/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java b/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java
new file mode 100644
index 00000000000..4595db7ba52
--- /dev/null
+++ b/test/jdk/jdk/dynalink/TestGuardingDynamicLinkerExporter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.  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<CallSiteDescriptor> 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<GuardingDynamicLinker> 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
index 00000000000..560c307b1ce
--- /dev/null
+++ b/test/jdk/jdk/dynalink/TrustedDynamicLinkerFactoryTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.  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
index 00000000000..866aacbf6cb
--- /dev/null
+++ b/test/jdk/jdk/dynalink/UntrustedDynamicLinkerFactoryTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.  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<ServiceConfigurationError> 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
index 00000000000..34bcd92e3bb
--- /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
index 00000000000..e3ff71034d9
--- /dev/null
+++ b/test/jdk/jdk/dynalink/untrusted.security.policy
@@ -0,0 +1,3 @@
+grant {
+  permission java.io.FilePermission "${user.home}/-", "read";
+};