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