1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 import static jdk.dynalink.StandardNamespace.PROPERTY;
  26 import static jdk.dynalink.StandardOperation.GET;
  27 
  28 import java.lang.invoke.CallSite;
  29 import java.lang.invoke.MethodHandle;
  30 import java.lang.invoke.MethodHandles;
  31 import java.lang.invoke.MethodType;
  32 import jdk.dynalink.CallSiteDescriptor;
  33 import jdk.dynalink.DynamicLinker;
  34 import jdk.dynalink.DynamicLinkerFactory;
  35 import jdk.dynalink.NoSuchDynamicMethodException;
  36 import jdk.dynalink.Operation;
  37 import jdk.dynalink.StandardNamespace;
  38 import jdk.dynalink.StandardOperation;
  39 import jdk.dynalink.beans.StaticClass;
  40 import jdk.dynalink.linker.GuardedInvocation;
  41 import jdk.dynalink.linker.LinkRequest;
  42 import jdk.dynalink.linker.LinkerServices;
  43 import jdk.dynalink.support.SimpleRelinkableCallSite;
  44 import org.testng.Assert;
  45 import org.testng.annotations.Test;
  46 
  47 /**
  48  * @test
  49  * @build TestGuardingDynamicLinkerExporter
  50  * @run testng/othervm/java.security.policy=trusted.security.policy TrustedDynamicLinkerFactoryTest
  51  */
  52 public class TrustedDynamicLinkerFactoryTest {
  53 
  54     private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY);
  55 
  56     private static DynamicLinkerFactory newDynamicLinkerFactory(final boolean resetClassLoader) {
  57         final DynamicLinkerFactory factory = new DynamicLinkerFactory();
  58         if (resetClassLoader) {
  59             factory.setClassLoader(null);
  60         }
  61         return factory;
  62     }
  63 
  64     @Test
  65     public void callSiteCreationTest() {
  66         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
  67         final DynamicLinker linker = factory.createLinker();
  68         final StandardOperation[] operations = StandardOperation.values();
  69         final MethodType mt = MethodType.methodType(Object.class, Object.class);
  70         for (final Operation op : operations) {
  71             final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
  72                     MethodHandles.publicLookup(), op, mt)));
  73             Assert.assertNotNull(cs);
  74             Assert.assertEquals(cs.type(), mt);
  75             Assert.assertNotNull(cs.getTarget());
  76         }
  77     }
  78 
  79     @Test
  80     public void fallbackLinkerTest() {
  81         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
  82         final Operation myOperation = new Operation() {
  83         };
  84         final boolean[] reachedFallback = { false };
  85         factory.setFallbackLinkers((final LinkRequest linkRequest, final LinkerServices linkerServices) -> {
  86             Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation);
  87             reachedFallback[0] = true;
  88             return null;
  89         });
  90 
  91         final DynamicLinker linker = factory.createLinker();
  92         final MethodType mt = MethodType.methodType(Object.class);
  93         final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
  94                 MethodHandles.publicLookup(), myOperation, mt)));
  95 
  96         // linking the call site initially does not invoke the linkers!
  97         Assert.assertFalse(reachedFallback[0]);
  98         try {
  99             cs.getTarget().invoke();
 100         } catch (final NoSuchDynamicMethodException nsdm) {
 101             // we do expect NoSuchDynamicMethod!
 102             // because our dummy fallback linker returns null!
 103         } catch (final Throwable th) {
 104             throw new RuntimeException("should not reach here with: " + th);
 105         }
 106 
 107         // check that the control reached fallback linker!
 108         Assert.assertTrue(reachedFallback[0]);
 109     }
 110 
 111     @Test
 112     public void priorityLinkerTest() {
 113         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
 114         final Operation myOperation = new Operation() {
 115         };
 116         final boolean[] reachedProrityLinker = { false };
 117         factory.setPrioritizedLinker((final LinkRequest linkRequest, final LinkerServices linkerServices) -> {
 118             Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation);
 119             reachedProrityLinker[0] = true;
 120             return null;
 121         });
 122 
 123         final DynamicLinker linker = factory.createLinker();
 124         final MethodType mt = MethodType.methodType(Object.class);
 125         final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
 126                 MethodHandles.publicLookup(), myOperation, mt)));
 127 
 128         // linking the call site initially does not invoke the linkers!
 129         Assert.assertFalse(reachedProrityLinker[0]);
 130         try {
 131             cs.getTarget().invoke();
 132         } catch (final NoSuchDynamicMethodException nsdm) {
 133             // we do expect NoSuchDynamicMethod!
 134             // because our dummy priority linker returns null!
 135         } catch (final Throwable th) {
 136             throw new RuntimeException("should not reach here with: " + th);
 137         }
 138 
 139         // check that the control reached fallback linker!
 140         Assert.assertTrue(reachedProrityLinker[0]);
 141     }
 142 
 143     @Test
 144     public void priorityAndFallbackLinkerTest() {
 145         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
 146         final Operation myOperation = new Operation() {
 147         };
 148         final int[] linkerReachCounter = { 0 };
 149         factory.setPrioritizedLinker((final LinkRequest linkRequest, final LinkerServices linkerServices) -> {
 150             Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation);
 151             linkerReachCounter[0]++;
 152             return null;
 153         });
 154         factory.setFallbackLinkers((final LinkRequest linkRequest, final LinkerServices linkerServices) -> {
 155             Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation);
 156             Assert.assertEquals(linkerReachCounter[0], 1);
 157             linkerReachCounter[0]++;
 158             return null;
 159         });
 160 
 161         final DynamicLinker linker = factory.createLinker();
 162         final MethodType mt = MethodType.methodType(Object.class);
 163         final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
 164                 MethodHandles.publicLookup(), myOperation, mt)));
 165 
 166         // linking the call site initially does not invoke the linkers!
 167         Assert.assertEquals(linkerReachCounter[0], 0);
 168 
 169         try {
 170             cs.getTarget().invoke();
 171         } catch (final NoSuchDynamicMethodException nsdm) {
 172             // we do expect NoSuchDynamicMethod!
 173         } catch (final Throwable th) {
 174             throw new RuntimeException("should not reach here with: " + th);
 175         }
 176 
 177         Assert.assertEquals(linkerReachCounter[0], 2);
 178     }
 179 
 180     @Test
 181     public void prelinkTransformerTest() throws Throwable {
 182         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
 183         final boolean[] reachedPrelinkTransformer = { false };
 184 
 185         factory.setPrelinkTransformer((final GuardedInvocation inv, final LinkRequest linkRequest, final LinkerServices linkerServices) -> {
 186             reachedPrelinkTransformer[0] = true;
 187             // just identity transformer!
 188             return inv;
 189         });
 190 
 191         final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
 192         final DynamicLinker linker = factory.createLinker();
 193         final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
 194                 MethodHandles.publicLookup(), GET_PROPERTY, mt)));
 195         Assert.assertFalse(reachedPrelinkTransformer[0]);
 196         Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class);
 197         Assert.assertTrue(reachedPrelinkTransformer[0]);
 198     }
 199 
 200     @Test
 201     public void internalObjectsFilterTest() throws Throwable {
 202         final DynamicLinkerFactory factory = newDynamicLinkerFactory(true);
 203         final boolean[] reachedInternalObjectsFilter = { false };
 204 
 205         factory.setInternalObjectsFilter((final MethodHandle mh) -> {
 206             reachedInternalObjectsFilter[0] = true;
 207             return mh;
 208         });
 209 
 210         final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
 211         final DynamicLinker linker = factory.createLinker();
 212         final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
 213                 MethodHandles.publicLookup(), GET_PROPERTY, mt)));
 214         Assert.assertFalse(reachedInternalObjectsFilter[0]);
 215         Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class);
 216         Assert.assertTrue(reachedInternalObjectsFilter[0]);
 217     }
 218 
 219     @Test
 220     public void autoLoadedLinkerTest() {
 221         testAutoLoadedLinkerInvoked(new Object(), "toString");
 222     }
 223 
 224     @Test
 225     public void autoLoadedLinkerSeesStaticMethod() {
 226         testAutoLoadedLinkerInvoked(StaticClass.forClass(System.class), "currentTimeMillis");
 227     }
 228 
 229     private static void testAutoLoadedLinkerInvoked(final Object target, final String methodName) {
 230         final DynamicLinkerFactory factory = newDynamicLinkerFactory(false);
 231         final DynamicLinker linker = factory.createLinker();
 232 
 233         final MethodType mt = MethodType.methodType(Object.class, Object.class);
 234         final CallSiteDescriptor testDescriptor = new CallSiteDescriptor(MethodHandles.publicLookup(),
 235                 GET.withNamespace(StandardNamespace.METHOD).named(methodName), mt);
 236         final CallSite cs = linker.link(new SimpleRelinkableCallSite(testDescriptor));
 237 
 238         TestGuardingDynamicLinkerExporter.enable();
 239         try {
 240             cs.getTarget().invoke(target);
 241             // The linker was loaded and it observed our invocation
 242             Assert.assertTrue(TestGuardingDynamicLinkerExporter.isLastCallSiteDescriptor(testDescriptor));
 243         } catch (final Throwable th) {
 244             throw new RuntimeException(th);
 245         } finally {
 246             TestGuardingDynamicLinkerExporter.disable();
 247         }
 248 
 249     }
 250 }