1 /*
2  * Copyright (c) 2020, 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 
newDynamicLinkerFactory(final boolean resetClassLoader)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
callSiteCreationTest()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
fallbackLinkerTest()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
priorityLinkerTest()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
priorityAndFallbackLinkerTest()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
prelinkTransformerTest()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
internalObjectsFilterTest()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
autoLoadedLinkerTest()220     public void autoLoadedLinkerTest() {
221         testAutoLoadedLinkerInvoked(new Object(), "toString");
222     }
223 
224     @Test
autoLoadedLinkerSeesStaticMethod()225     public void autoLoadedLinkerSeesStaticMethod() {
226         testAutoLoadedLinkerInvoked(StaticClass.forClass(System.class), "currentTimeMillis");
227     }
228 
testAutoLoadedLinkerInvoked(final Object target, final String methodName)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 }
251