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 package jdk.dynalink.test; 26 27 import static jdk.dynalink.StandardNamespace.PROPERTY; 28 import static jdk.dynalink.StandardOperation.GET; 29 30 import java.lang.invoke.CallSite; 31 import java.lang.invoke.MethodHandle; 32 import java.lang.invoke.MethodHandles; 33 import java.lang.invoke.MethodType; 34 import java.util.List; 35 import java.util.ServiceConfigurationError; 36 import javax.script.ScriptEngine; 37 import javax.script.ScriptEngineManager; 38 import jdk.dynalink.CallSiteDescriptor; 39 import jdk.dynalink.DynamicLinker; 40 import jdk.dynalink.DynamicLinkerFactory; 41 import jdk.dynalink.NoSuchDynamicMethodException; 42 import jdk.dynalink.Operation; 43 import jdk.dynalink.StandardNamespace; 44 import jdk.dynalink.StandardOperation; 45 import jdk.dynalink.beans.StaticClass; 46 import jdk.dynalink.linker.GuardedInvocation; 47 import jdk.dynalink.linker.GuardingDynamicLinker; 48 import jdk.dynalink.linker.LinkRequest; 49 import jdk.dynalink.linker.LinkerServices; 50 import jdk.dynalink.support.SimpleRelinkableCallSite; 51 import jdk.nashorn.api.scripting.AbstractJSObject; 52 import org.testng.Assert; 53 import org.testng.annotations.Test; 54 55 @SuppressWarnings("javadoc") 56 public class DynamicLinkerFactoryTest { 57 58 private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY); 59 newDynamicLinkerFactory(final boolean resetClassLoader)60 private static DynamicLinkerFactory newDynamicLinkerFactory(final boolean resetClassLoader) { 61 final DynamicLinkerFactory factory = new DynamicLinkerFactory(); 62 if (resetClassLoader) { 63 factory.setClassLoader(null); 64 } 65 return factory; 66 } 67 68 @Test callSiteCreationTest()69 public void callSiteCreationTest() { 70 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 71 final DynamicLinker linker = factory.createLinker(); 72 final StandardOperation[] operations = StandardOperation.values(); 73 final MethodType mt = MethodType.methodType(Object.class, Object.class); 74 for (final Operation op : operations) { 75 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 76 MethodHandles.publicLookup(), op, mt))); 77 Assert.assertNotNull(cs); 78 Assert.assertEquals(cs.type(), mt); 79 Assert.assertNotNull(cs.getTarget()); 80 } 81 } 82 83 @Test fallbackLinkerTest()84 public void fallbackLinkerTest() { 85 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 86 final Operation myOperation = new Operation() { 87 }; 88 final boolean[] reachedFallback = { false }; 89 factory.setFallbackLinkers((GuardingDynamicLinker) (final LinkRequest linkRequest, final LinkerServices linkerServices) -> { 90 Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); 91 reachedFallback[0] = true; 92 return null; 93 }); 94 95 final DynamicLinker linker = factory.createLinker(); 96 final MethodType mt = MethodType.methodType(Object.class); 97 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 98 MethodHandles.publicLookup(), myOperation, mt))); 99 100 // linking the call site initially does not invoke the linkers! 101 Assert.assertFalse(reachedFallback[0]); 102 try { 103 cs.getTarget().invoke(); 104 } catch (final NoSuchDynamicMethodException nsdm) { 105 // we do expect NoSuchDynamicMethod! 106 // because our dummy fallback linker returns null! 107 } catch (final Throwable th) { 108 throw new RuntimeException("should not reach here with: " + th); 109 } 110 111 // check that the control reached fallback linker! 112 Assert.assertTrue(reachedFallback[0]); 113 } 114 115 @Test priorityLinkerTest()116 public void priorityLinkerTest() { 117 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 118 final Operation myOperation = new Operation() { 119 }; 120 final boolean[] reachedProrityLinker = { false }; 121 factory.setPrioritizedLinker((GuardingDynamicLinker) (final LinkRequest linkRequest, final LinkerServices linkerServices) -> { 122 Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); 123 reachedProrityLinker[0] = true; 124 return null; 125 }); 126 127 final DynamicLinker linker = factory.createLinker(); 128 final MethodType mt = MethodType.methodType(Object.class); 129 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 130 MethodHandles.publicLookup(), myOperation, mt))); 131 132 // linking the call site initially does not invoke the linkers! 133 Assert.assertFalse(reachedProrityLinker[0]); 134 try { 135 cs.getTarget().invoke(); 136 } catch (final NoSuchDynamicMethodException nsdm) { 137 // we do expect NoSuchDynamicMethod! 138 // because our dummy priority linker returns null! 139 } catch (final Throwable th) { 140 throw new RuntimeException("should not reach here with: " + th); 141 } 142 143 // check that the control reached fallback linker! 144 Assert.assertTrue(reachedProrityLinker[0]); 145 } 146 147 @Test priorityAndFallbackLinkerTest()148 public void priorityAndFallbackLinkerTest() { 149 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 150 final Operation myOperation = new Operation() { 151 }; 152 final int[] linkerReachCounter = { 0 }; 153 factory.setPrioritizedLinker((GuardingDynamicLinker) (final LinkRequest linkRequest, final LinkerServices linkerServices) -> { 154 Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); 155 linkerReachCounter[0]++; 156 return null; 157 }); 158 factory.setFallbackLinkers((GuardingDynamicLinker) (final LinkRequest linkRequest, final LinkerServices linkerServices) -> { 159 Assert.assertEquals(linkRequest.getCallSiteDescriptor().getOperation(), myOperation); 160 Assert.assertEquals(linkerReachCounter[0], 1); 161 linkerReachCounter[0]++; 162 return null; 163 }); 164 165 final DynamicLinker linker = factory.createLinker(); 166 final MethodType mt = MethodType.methodType(Object.class); 167 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 168 MethodHandles.publicLookup(), myOperation, mt))); 169 170 // linking the call site initially does not invoke the linkers! 171 Assert.assertEquals(linkerReachCounter[0], 0); 172 173 try { 174 cs.getTarget().invoke(); 175 } catch (final NoSuchDynamicMethodException nsdm) { 176 // we do expect NoSuchDynamicMethod! 177 } catch (final Throwable th) { 178 throw new RuntimeException("should not reach here with: " + th); 179 } 180 181 Assert.assertEquals(linkerReachCounter[0], 2); 182 } 183 184 @Test prelinkTransformerTest()185 public void prelinkTransformerTest() throws Throwable { 186 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 187 final boolean[] reachedPrelinkTransformer = { false }; 188 189 factory.setPrelinkTransformer((final GuardedInvocation inv, final LinkRequest linkRequest, final LinkerServices linkerServices) -> { 190 reachedPrelinkTransformer[0] = true; 191 // just identity transformer! 192 return inv; 193 }); 194 195 final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); 196 final DynamicLinker linker = factory.createLinker(); 197 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 198 MethodHandles.publicLookup(), GET_PROPERTY, mt))); 199 Assert.assertFalse(reachedPrelinkTransformer[0]); 200 Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class); 201 Assert.assertTrue(reachedPrelinkTransformer[0]); 202 } 203 204 @Test internalObjectsFilterTest()205 public void internalObjectsFilterTest() throws Throwable { 206 final DynamicLinkerFactory factory = newDynamicLinkerFactory(true); 207 final boolean[] reachedInternalObjectsFilter = { false }; 208 209 factory.setInternalObjectsFilter((final MethodHandle mh) -> { 210 reachedInternalObjectsFilter[0] = true; 211 return mh; 212 }); 213 214 final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class); 215 final DynamicLinker linker = factory.createLinker(); 216 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 217 MethodHandles.publicLookup(), GET_PROPERTY, mt))); 218 Assert.assertFalse(reachedInternalObjectsFilter[0]); 219 Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class); 220 Assert.assertTrue(reachedInternalObjectsFilter[0]); 221 } 222 checkOneAutoLoadingError(final DynamicLinkerFactory factory)223 private static void checkOneAutoLoadingError(final DynamicLinkerFactory factory) { 224 // expect one error as we have one untrusted linker exporter in META-INF/services 225 final List<ServiceConfigurationError> autoLoadingErrors = factory.getAutoLoadingErrors(); 226 // single error ... 227 Assert.assertFalse(autoLoadingErrors.isEmpty()); 228 final Throwable cause = autoLoadingErrors.get(0).getCause(); 229 // .. due to permission check.. 230 Assert.assertTrue(cause.toString().contains("dynalink.exportLinkersAutomatically")); 231 } 232 233 @Test autoLoadedLinkerNegativeTest()234 public void autoLoadedLinkerNegativeTest() { 235 // enable auto loaded linkers 236 final DynamicLinkerFactory factory = newDynamicLinkerFactory(false); 237 factory.createLinker(); 238 checkOneAutoLoadingError(factory); 239 } 240 241 @Test autoLoadedLinkerTest()242 public void autoLoadedLinkerTest() { 243 testAutoLoadedLinkerInvoked(new Object(), "toString"); 244 } 245 246 @Test autoLoadedLinkerSeesStaticMethod()247 public void autoLoadedLinkerSeesStaticMethod() { 248 testAutoLoadedLinkerInvoked(StaticClass.forClass(System.class), "currentTimeMillis"); 249 } 250 testAutoLoadedLinkerInvoked(final Object target, final String methodName)251 private static void testAutoLoadedLinkerInvoked(final Object target, final String methodName) { 252 final DynamicLinkerFactory factory = newDynamicLinkerFactory(false); 253 final DynamicLinker linker = factory.createLinker(); 254 255 // we should still get one error due to untrusted dynamic linker exporter! 256 checkOneAutoLoadingError(factory); 257 258 final MethodType mt = MethodType.methodType(Object.class, Object.class); 259 final CallSiteDescriptor testDescriptor = new CallSiteDescriptor(MethodHandles.publicLookup(), 260 GET.withNamespace(StandardNamespace.METHOD).named(methodName), mt); 261 final CallSite cs = linker.link(new SimpleRelinkableCallSite(testDescriptor)); 262 263 TrustedGuardingDynamicLinkerExporter.enable(); 264 try { 265 cs.getTarget().invoke(target); 266 // The linker was loaded and it observed our invocation 267 Assert.assertTrue(TrustedGuardingDynamicLinkerExporter.isLastCallSiteDescriptor(testDescriptor)); 268 } catch (final Throwable th) { 269 throw new RuntimeException(th); 270 } finally { 271 TrustedGuardingDynamicLinkerExporter.disable(); 272 } 273 274 } 275 276 @Test nashornExportedLinkerJSObjectTest()277 public void nashornExportedLinkerJSObjectTest() { 278 final DynamicLinkerFactory factory = newDynamicLinkerFactory(false); 279 final DynamicLinker linker = factory.createLinker(); 280 281 final MethodType mt = MethodType.methodType(Object.class, Object.class); 282 final Operation op = GET_PROPERTY.named("foo"); 283 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 284 MethodHandles.publicLookup(), op, mt))); 285 final boolean[] reachedGetMember = new boolean[1]; 286 // check that the nashorn exported linker can be used for user defined JSObject 287 final Object obj = new AbstractJSObject() { 288 @Override 289 public Object getMember(final String name) { 290 reachedGetMember[0] = true; 291 return name.equals("foo")? "bar" : "<unknown>"; 292 } 293 }; 294 295 Object value = null; 296 try { 297 value = cs.getTarget().invoke(obj); 298 } catch (final Throwable th) { 299 throw new RuntimeException(th); 300 } 301 302 Assert.assertTrue(reachedGetMember[0]); 303 Assert.assertEquals(value, "bar"); 304 } 305 306 @Test nashornExportedLinkerScriptObjectMirrorTest()307 public void nashornExportedLinkerScriptObjectMirrorTest() { 308 final DynamicLinkerFactory factory = newDynamicLinkerFactory(false); 309 final DynamicLinker linker = factory.createLinker(); 310 311 // check that the nashorn exported linker can be used for ScriptObjectMirror 312 final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 313 final MethodType mt = MethodType.methodType(Object.class, Object.class); 314 final Operation op = GET_PROPERTY.named("foo"); 315 final CallSite cs = linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor( 316 MethodHandles.publicLookup(), op, mt))); 317 Object value = null; 318 try { 319 final Object obj = engine.eval("({ foo: 'hello' })"); 320 value = cs.getTarget().invoke(obj); 321 } catch (final Throwable th) { 322 throw new RuntimeException(th); 323 } 324 Assert.assertEquals(value, "hello"); 325 } 326 } 327