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.beans.test;
26 
27 import static jdk.dynalink.StandardNamespace.ELEMENT;
28 import static jdk.dynalink.StandardNamespace.METHOD;
29 import static jdk.dynalink.StandardNamespace.PROPERTY;
30 import static jdk.dynalink.StandardOperation.CALL;
31 import static jdk.dynalink.StandardOperation.GET;
32 import static jdk.dynalink.StandardOperation.NEW;
33 import static jdk.dynalink.StandardOperation.REMOVE;
34 import static jdk.dynalink.StandardOperation.SET;
35 
36 import java.lang.invoke.CallSite;
37 import java.lang.invoke.MethodHandle;
38 import java.lang.invoke.MethodHandles;
39 import java.lang.invoke.MethodType;
40 import java.security.AccessControlException;
41 import java.util.ArrayList;
42 import java.util.Date;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 import jdk.dynalink.CallSiteDescriptor;
47 import jdk.dynalink.DynamicLinker;
48 import jdk.dynalink.DynamicLinkerFactory;
49 import jdk.dynalink.NamedOperation;
50 import jdk.dynalink.NoSuchDynamicMethodException;
51 import jdk.dynalink.Operation;
52 import jdk.dynalink.beans.BeansLinker;
53 import jdk.dynalink.beans.StaticClass;
54 import jdk.dynalink.support.SimpleRelinkableCallSite;
55 import org.testng.Assert;
56 import org.testng.annotations.AfterTest;
57 import org.testng.annotations.BeforeTest;
58 import org.testng.annotations.DataProvider;
59 import org.testng.annotations.Test;
60 
61 public class BeanLinkerTest {
62 
63     private DynamicLinker linker;
64     private static final MethodHandles.Lookup MY_LOOKUP = MethodHandles.lookup();
65 
66     @SuppressWarnings("unused")
67     @DataProvider
flags()68     private static Object[][] flags() {
69         return new Object[][]{
70             {Boolean.FALSE},
71             {Boolean.TRUE}
72         };
73     }
74 
75     // helpers to create callsite objects
createCallSite(final boolean publicLookup, final Operation op, final MethodType mt)76     private CallSite createCallSite(final boolean publicLookup, final Operation op, final MethodType mt) {
77         return linker.link(new SimpleRelinkableCallSite(new CallSiteDescriptor(
78                 publicLookup ? MethodHandles.publicLookup() : MY_LOOKUP, op, mt)));
79     }
80 
createCallSite(final boolean publicLookup, final Operation op, final Object name, final MethodType mt)81     private CallSite createCallSite(final boolean publicLookup, final Operation op, final Object name, final MethodType mt) {
82         return createCallSite(publicLookup, op.named(name), mt);
83     }
84 
createGetMethodCallSite(final boolean publicLookup, final String name)85     private CallSite createGetMethodCallSite(final boolean publicLookup, final String name) {
86         return createCallSite(publicLookup, GET_METHOD, name, MethodType.methodType(Object.class, Object.class));
87     }
88 
89     private static final MethodHandle throwArrayIndexOutOfBounds = findThrower("throwArrayIndexOutOfBounds");
90     private static final MethodHandle throwIndexOutOfBounds = findThrower("throwIndexOutOfBounds");
91 
92     private static final Operation GET_PROPERTY = GET.withNamespace(PROPERTY);
93     private static final Operation GET_ELEMENT = GET.withNamespace(ELEMENT);
94     private static final Operation GET_METHOD = GET.withNamespace(METHOD);
95     private static final Operation SET_ELEMENT = SET.withNamespace(ELEMENT);
96     private static final Operation REMOVE_ELEMENT = REMOVE.withNamespace(ELEMENT);
97 
findThrower(final String name)98     private static final MethodHandle findThrower(final String name) {
99         try {
100             return MethodHandles.lookup().findStatic(BeanLinkerTest.class, name,
101                     MethodType.methodType(Object.class, Object.class, Object.class));
102         } catch (NoSuchMethodException | IllegalAccessException e) {
103             Assert.fail("Unexpected exception", e);
104             return null;
105         }
106     }
107 
throwArrayIndexOutOfBounds(final Object receiver, final Object index)108     private static Object throwArrayIndexOutOfBounds(final Object receiver, final Object index) {
109         throw new ArrayIndexOutOfBoundsException(String.valueOf(index));
110     }
111 
throwIndexOutOfBounds(final Object receiver, final Object index)112     private static Object throwIndexOutOfBounds(final Object receiver, final Object index) {
113         throw new IndexOutOfBoundsException(String.valueOf(index));
114     }
115 
116     @BeforeTest
initLinker()117     public void initLinker() {
118         final DynamicLinkerFactory factory = new DynamicLinkerFactory();
119         factory.setFallbackLinkers(new BeansLinker((req, services) -> {
120             // This is a MissingMemberHandlerFactory that creates a missing
121             // member handler for element getters and setters that throw an
122             // ArrayIndexOutOfBoundsException when applied to an array and an
123             // IndexOutOfBoundsException when applied to a list.
124 
125             final CallSiteDescriptor desc = req.getCallSiteDescriptor();
126             final Operation op = desc.getOperation();
127             final Operation baseOp = NamedOperation.getBaseOperation(op);
128             if (baseOp != GET_ELEMENT && baseOp != SET_ELEMENT && baseOp != REMOVE_ELEMENT) {
129                 // We only handle GET_ELEMENT, SET_ELEMENT and REMOVE_ELEMENT.
130                 return null;
131             }
132 
133             final Object receiver = req.getReceiver();
134             Assert.assertNotNull(receiver);
135 
136             final Class<?> clazz = receiver.getClass();
137             final MethodHandle throwerHandle;
138             if (clazz.isArray()) {
139                 throwerHandle = throwArrayIndexOutOfBounds;
140             } else if (List.class.isAssignableFrom(clazz)) {
141                 throwerHandle = throwIndexOutOfBounds;
142             } else {
143                 Assert.fail("Unexpected receiver type " + clazz.getName());
144                 return null;
145             }
146 
147             final Object name = NamedOperation.getName(op);
148             final MethodHandle nameBoundHandle;
149             if (name == null) {
150                 nameBoundHandle = throwerHandle;
151             } else {
152                 // If the operation is for a fixed index, bind it
153                 nameBoundHandle = MethodHandles.insertArguments(throwerHandle, 1, name);
154             }
155 
156             final MethodType callSiteType = desc.getMethodType();
157             final MethodHandle arityMatchedHandle;
158             if (baseOp == SET_ELEMENT) {
159                 // Drop "value" parameter for a setter
160                 final int handleArity = nameBoundHandle.type().parameterCount();
161                 arityMatchedHandle = MethodHandles.dropArguments(nameBoundHandle,
162                         handleArity, callSiteType.parameterType(handleArity));
163             } else {
164                 arityMatchedHandle = nameBoundHandle;
165             }
166 
167             return arityMatchedHandle.asType(callSiteType);
168         }));
169         this.linker = factory.createLinker();
170     }
171 
172     @AfterTest
afterTest()173     public void afterTest() {
174         this.linker = null;
175     }
176 
177     @Test(dataProvider = "flags")
getPropertyTest(final boolean publicLookup)178     public void getPropertyTest(final boolean publicLookup) throws Throwable {
179         final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
180         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
181         Assert.assertEquals(cs.getTarget().invoke(new Object(), "class"), Object.class);
182         Assert.assertEquals(cs.getTarget().invoke(new Date(), "class"), Date.class);
183     }
184 
185     @Test(dataProvider = "flags")
getPropertyNegativeTest(final boolean publicLookup)186     public void getPropertyNegativeTest(final boolean publicLookup) throws Throwable {
187         final MethodType mt = MethodType.methodType(Object.class, Object.class, String.class);
188         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
189         Assert.assertNull(cs.getTarget().invoke(new Object(), "DOES_NOT_EXIST"));
190     }
191 
192     @Test(dataProvider = "flags")
getPropertyTest2(final boolean publicLookup)193     public void getPropertyTest2(final boolean publicLookup) throws Throwable {
194         final MethodType mt = MethodType.methodType(Object.class, Object.class);
195         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "class", mt);
196         Assert.assertEquals(cs.getTarget().invoke(new Object()), Object.class);
197         Assert.assertEquals(cs.getTarget().invoke(new Date()), Date.class);
198     }
199 
200     @Test(dataProvider = "flags")
getPropertyNegativeTest2(final boolean publicLookup)201     public void getPropertyNegativeTest2(final boolean publicLookup) throws Throwable {
202         final MethodType mt = MethodType.methodType(Object.class, Object.class);
203         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "DOES_NOT_EXIST", mt);
204 
205         try {
206             cs.getTarget().invoke(new Object());
207             throw new RuntimeException("Expected NoSuchDynamicMethodException");
208         } catch (final Throwable th) {
209             Assert.assertTrue(th instanceof NoSuchDynamicMethodException);
210         }
211     }
212 
213     @Test(dataProvider = "flags")
getLengthPropertyTest(final boolean publicLookup)214     public void getLengthPropertyTest(final boolean publicLookup) throws Throwable {
215         final MethodType mt = MethodType.methodType(int.class, Object.class, String.class);
216         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, mt);
217 
218         Assert.assertEquals((int) cs.getTarget().invoke(new int[10], "length"), 10);
219         Assert.assertEquals((int) cs.getTarget().invoke(new String[33], "length"), 33);
220     }
221 
222     @Test(dataProvider = "flags")
getElementTest(final boolean publicLookup)223     public void getElementTest(final boolean publicLookup) throws Throwable {
224         final MethodType mt = MethodType.methodType(int.class, Object.class, int.class);
225         final CallSite cs = createCallSite(publicLookup, GET_ELEMENT, mt);
226 
227         final int[] arr = {23, 42};
228         Assert.assertEquals((int) cs.getTarget().invoke(arr, 0), 23);
229         Assert.assertEquals((int) cs.getTarget().invoke(arr, 1), 42);
230         try {
231             final int x = (int) cs.getTarget().invoke(arr, -1);
232             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
233         } catch (final ArrayIndexOutOfBoundsException ex) {
234         }
235 
236         try {
237             final int x = (int) cs.getTarget().invoke(arr, arr.length);
238             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
239         } catch (final ArrayIndexOutOfBoundsException ex) {
240         }
241 
242         final List<Integer> list = new ArrayList<>();
243         list.add(23);
244         list.add(430);
245         list.add(-4354);
246         Assert.assertEquals((int) cs.getTarget().invoke(list, 0), (int) list.get(0));
247         Assert.assertEquals((int) cs.getTarget().invoke(list, 1), (int) list.get(1));
248         Assert.assertEquals((int) cs.getTarget().invoke(list, 2), (int) list.get(2));
249         try {
250             cs.getTarget().invoke(list, -1);
251             throw new RuntimeException("expected IndexOutOfBoundsException");
252         } catch (final IndexOutOfBoundsException ex) {
253         }
254 
255         try {
256             cs.getTarget().invoke(list, list.size());
257             throw new RuntimeException("expected IndexOutOfBoundsException");
258         } catch (final IndexOutOfBoundsException ex) {
259         }
260     }
261 
invokeWithFixedKey(boolean publicLookup, Operation op, Object name, MethodType mt, Object... args)262     private Object invokeWithFixedKey(boolean publicLookup, Operation op, Object name, MethodType mt, Object... args) throws Throwable {
263         return createCallSite(publicLookup, op.named(name), mt).getTarget().invokeWithArguments(args);
264     }
265 
266     @Test(dataProvider = "flags")
getElementWithFixedKeyTest(final boolean publicLookup)267     public void getElementWithFixedKeyTest(final boolean publicLookup) throws Throwable {
268         final MethodType mt = MethodType.methodType(int.class, Object.class);
269 
270         final int[] arr = {23, 42};
271         Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 0, mt, arr), 23);
272         Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 1, mt, arr), 42);
273         try {
274             invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, arr);
275             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
276         } catch (final ArrayIndexOutOfBoundsException ex) {
277         }
278 
279         try {
280             invokeWithFixedKey(publicLookup, GET_ELEMENT, arr.length, mt, arr);
281             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
282         } catch (final ArrayIndexOutOfBoundsException ex) {
283         }
284 
285         final List<Integer> list = new ArrayList<>();
286         list.add(23);
287         list.add(430);
288         list.add(-4354);
289         for (int i = 0; i < 3; ++i) {
290             Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, i, mt, list), (int) list.get(i));
291         }
292         try {
293             invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, list);
294             throw new RuntimeException("expected IndexOutOfBoundsException");
295         } catch (final IndexOutOfBoundsException ex) {
296         }
297 
298         try {
299             invokeWithFixedKey(publicLookup, GET_ELEMENT, list.size(), mt, list);
300             throw new RuntimeException("expected IndexOutOfBoundsException");
301         } catch (final IndexOutOfBoundsException ex) {
302         }
303     }
304 
305     @Test(dataProvider = "flags")
setElementTest(final boolean publicLookup)306     public void setElementTest(final boolean publicLookup) throws Throwable {
307         final MethodType mt = MethodType.methodType(void.class, Object.class, int.class, int.class);
308         final CallSite cs = createCallSite(publicLookup, SET_ELEMENT, mt);
309 
310         final int[] arr = {23, 42};
311         cs.getTarget().invoke(arr, 0, 0);
312         Assert.assertEquals(arr[0], 0);
313         cs.getTarget().invoke(arr, 1, -5);
314         Assert.assertEquals(arr[1], -5);
315 
316         try {
317             cs.getTarget().invoke(arr, -1, 12);
318             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
319         } catch (final ArrayIndexOutOfBoundsException ex) {
320         }
321 
322         try {
323             cs.getTarget().invoke(arr, arr.length, 20);
324             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
325         } catch (final ArrayIndexOutOfBoundsException ex) {
326         }
327 
328         final List<Integer> list = new ArrayList<>();
329         list.add(23);
330         list.add(430);
331         list.add(-4354);
332 
333         cs.getTarget().invoke(list, 0, -list.get(0));
334         Assert.assertEquals((int) list.get(0), -23);
335         cs.getTarget().invoke(list, 1, -430);
336         Assert.assertEquals((int) list.get(1), -430);
337         cs.getTarget().invoke(list, 2, 4354);
338         Assert.assertEquals((int) list.get(2), 4354);
339         try {
340             cs.getTarget().invoke(list, -1, 343);
341             throw new RuntimeException("expected IndexOutOfBoundsException");
342         } catch (final IndexOutOfBoundsException ex) {
343         }
344 
345         try {
346             cs.getTarget().invoke(list, list.size(), 43543);
347             throw new RuntimeException("expected IndexOutOfBoundsException");
348         } catch (final IndexOutOfBoundsException ex) {
349         }
350     }
351 
352     @Test(dataProvider = "flags")
setElementWithFixedKeyTest(final boolean publicLookup)353     public void setElementWithFixedKeyTest(final boolean publicLookup) throws Throwable {
354         final MethodType mt = MethodType.methodType(void.class, Object.class, int.class);
355 
356         final int[] arr = {23, 42};
357         invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, arr, 0);
358         Assert.assertEquals(arr[0], 0);
359         invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, arr, -5);
360         Assert.assertEquals(arr[1], -5);
361 
362         try {
363             invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, arr, 12);
364             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
365         } catch (final ArrayIndexOutOfBoundsException ex) {
366         }
367 
368         try {
369             invokeWithFixedKey(publicLookup, SET_ELEMENT, arr.length, mt, arr, 20);
370             throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
371         } catch (final ArrayIndexOutOfBoundsException ex) {
372         }
373 
374         final List<Integer> list = new ArrayList<>();
375         list.add(23);
376         list.add(430);
377         list.add(-4354);
378 
379         invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, list, -list.get(0));
380         Assert.assertEquals((int) list.get(0), -23);
381         invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, list, -430);
382         Assert.assertEquals((int) list.get(1), -430);
383         invokeWithFixedKey(publicLookup, SET_ELEMENT, 2, mt, list, 4354);
384         Assert.assertEquals((int) list.get(2), 4354);
385         try {
386             invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, list, 343);
387             throw new RuntimeException("expected IndexOutOfBoundsException");
388         } catch (final IndexOutOfBoundsException ex) {
389         }
390 
391         try {
392             invokeWithFixedKey(publicLookup, SET_ELEMENT, list.size(), mt, list, 43543);
393             throw new RuntimeException("expected IndexOutOfBoundsException");
394         } catch (final IndexOutOfBoundsException ex) {
395         }
396     }
397 
398     @Test(dataProvider = "flags")
newObjectTest(final boolean publicLookup)399     public void newObjectTest(final boolean publicLookup) {
400         final MethodType mt = MethodType.methodType(Object.class, Object.class);
401         final CallSite cs = createCallSite(publicLookup, NEW, mt);
402 
403         Object obj = null;
404         try {
405             obj = cs.getTarget().invoke(StaticClass.forClass(Date.class));
406         } catch (final Throwable th) {
407             throw new RuntimeException(th);
408         }
409 
410         Assert.assertTrue(obj instanceof Date);
411     }
412 
413     @Test(dataProvider = "flags")
staticPropertyTest(final boolean publicLookup)414     public void staticPropertyTest(final boolean publicLookup) {
415         final MethodType mt = MethodType.methodType(Object.class, Class.class);
416         final CallSite cs = createCallSite(publicLookup, GET_PROPERTY, "static", mt);
417 
418         Object obj = null;
419         try {
420             obj = cs.getTarget().invoke(Object.class);
421         } catch (final Throwable th) {
422             throw new RuntimeException(th);
423         }
424 
425         Assert.assertTrue(obj instanceof StaticClass);
426         Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Object.class);
427 
428         try {
429             obj = cs.getTarget().invoke(Date.class);
430         } catch (final Throwable th) {
431             throw new RuntimeException(th);
432         }
433 
434         Assert.assertTrue(obj instanceof StaticClass);
435         Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Date.class);
436 
437         try {
438             obj = cs.getTarget().invoke(Object[].class);
439         } catch (final Throwable th) {
440             throw new RuntimeException(th);
441         }
442 
443         Assert.assertTrue(obj instanceof StaticClass);
444         Assert.assertEquals(((StaticClass) obj).getRepresentedClass(), Object[].class);
445     }
446 
447     @Test(dataProvider = "flags")
instanceMethodCallTest(final boolean publicLookup)448     public void instanceMethodCallTest(final boolean publicLookup) {
449         final CallSite cs = createGetMethodCallSite(publicLookup, "getClass");
450         final MethodType mt2 = MethodType.methodType(Class.class, Object.class, Object.class);
451         final CallSite cs2 = createCallSite(publicLookup, CALL, mt2);
452 
453         Object method = null;
454         try {
455             method = cs.getTarget().invoke(new Date());
456         } catch (final Throwable th) {
457             throw new RuntimeException(th);
458         }
459 
460         Assert.assertNotNull(method);
461         Assert.assertTrue(BeansLinker.isDynamicMethod(method));
462         Class clz = null;
463         try {
464             clz = (Class) cs2.getTarget().invoke(method, new Date());
465         } catch (final Throwable th) {
466             throw new RuntimeException(th);
467         }
468 
469         Assert.assertEquals(clz, Date.class);
470     }
471 
472     @Test(dataProvider = "flags")
staticMethodCallTest(final boolean publicLookup)473     public void staticMethodCallTest(final boolean publicLookup) {
474         final CallSite cs = createGetMethodCallSite(publicLookup, "getProperty");
475         final MethodType mt2 = MethodType.methodType(String.class, Object.class, Object.class, String.class);
476         final CallSite cs2 = createCallSite(publicLookup, CALL, mt2);
477 
478         Object method = null;
479         try {
480             method = cs.getTarget().invoke(StaticClass.forClass(System.class));
481         } catch (final Throwable th) {
482             throw new RuntimeException(th);
483         }
484 
485         Assert.assertNotNull(method);
486         Assert.assertTrue(BeansLinker.isDynamicMethod(method));
487 
488         String str = null;
489         try {
490             str = (String) cs2.getTarget().invoke(method, null, "os.name");
491         } catch (final Throwable th) {
492             throw new RuntimeException(th);
493         }
494         Assert.assertEquals(str, System.getProperty("os.name"));
495     }
496 
497     // try calling System.getenv and expect security exception
498     @Test(dataProvider = "flags")
systemGetenvTest(final boolean publicLookup)499     public void systemGetenvTest(final boolean publicLookup) {
500         final CallSite cs1 = createGetMethodCallSite(publicLookup, "getenv");
501         final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(Object.class, Object.class, Object.class));
502 
503         try {
504             final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class));
505             cs2.getTarget().invoke(method, StaticClass.forClass(System.class));
506             throw new RuntimeException("should not reach here in any case!");
507         } catch (final Throwable th) {
508             Assert.assertTrue(th instanceof SecurityException);
509         }
510     }
511 
512     // try getting a specific sensitive System property and expect security exception
513     @Test(dataProvider = "flags")
systemGetPropertyTest(final boolean publicLookup)514     public void systemGetPropertyTest(final boolean publicLookup) {
515         final CallSite cs1 = createGetMethodCallSite(publicLookup, "getProperty");
516         final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(String.class, Object.class, Object.class, String.class));
517 
518         try {
519             final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class));
520             cs2.getTarget().invoke(method, StaticClass.forClass(System.class), "java.home");
521             throw new RuntimeException("should not reach here in any case!");
522         } catch (final Throwable th) {
523             Assert.assertTrue(th instanceof SecurityException);
524         }
525     }
526 
527     // check a @CallerSensitive API and expect appropriate access check exception
528     @Test(dataProvider = "flags")
systemLoadLibraryTest(final boolean publicLookup)529     public void systemLoadLibraryTest(final boolean publicLookup) {
530         final CallSite cs1 = createGetMethodCallSite(publicLookup, "loadLibrary");
531         final CallSite cs2 = createCallSite(publicLookup, CALL, MethodType.methodType(void.class, Object.class, Object.class, String.class));
532 
533         try {
534             final Object method = cs1.getTarget().invoke(StaticClass.forClass(System.class));
535             cs2.getTarget().invoke(method, StaticClass.forClass(System.class), "foo");
536             throw new RuntimeException("should not reach here in any case!");
537         } catch (final Throwable th) {
538             if (publicLookup) {
539                 Assert.assertTrue(th instanceof IllegalAccessError);
540             } else {
541                 Assert.assertTrue(th instanceof AccessControlException);
542             }
543         }
544     }
545 
546     @Test(dataProvider = "flags")
removeElementFromListTest(final boolean publicLookup)547     public void removeElementFromListTest(final boolean publicLookup) throws Throwable {
548         final MethodType mt = MethodType.methodType(void.class, Object.class, int.class);
549         final CallSite cs = createCallSite(publicLookup, REMOVE_ELEMENT, mt);
550 
551         final List<Integer> list = new ArrayList<>(List.of(23, 430, -4354));
552 
553         cs.getTarget().invoke(list, 1);
554         Assert.assertEquals(list, List.of(23, -4354));
555         cs.getTarget().invoke(list, 1);
556         Assert.assertEquals(list, List.of(23));
557         cs.getTarget().invoke(list, 0);
558         Assert.assertEquals(list, List.of());
559         try {
560             cs.getTarget().invoke(list, -1);
561             throw new RuntimeException("expected IndexOutOfBoundsException");
562         } catch (final IndexOutOfBoundsException ex) {
563         }
564 
565         try {
566             cs.getTarget().invoke(list, list.size());
567             throw new RuntimeException("expected IndexOutOfBoundsException");
568         } catch (final IndexOutOfBoundsException ex) {
569         }
570     }
571 
572     @Test(dataProvider = "flags")
removeElementFromListWithFixedKeyTest(final boolean publicLookup)573     public void removeElementFromListWithFixedKeyTest(final boolean publicLookup) throws Throwable {
574         final MethodType mt = MethodType.methodType(void.class, Object.class);
575 
576         final List<Integer> list = new ArrayList<>(List.of(23, 430, -4354));
577 
578         createCallSite(publicLookup, REMOVE_ELEMENT.named(1), mt).getTarget().invoke(list);
579         Assert.assertEquals(list, List.of(23, -4354));
580         createCallSite(publicLookup, REMOVE_ELEMENT.named(1), mt).getTarget().invoke(list);
581         Assert.assertEquals(list, List.of(23));
582         createCallSite(publicLookup, REMOVE_ELEMENT.named(0), mt).getTarget().invoke(list);
583         Assert.assertEquals(list, List.of());
584         try {
585             createCallSite(publicLookup, REMOVE_ELEMENT.named(-1), mt).getTarget().invoke(list);
586             throw new RuntimeException("expected IndexOutOfBoundsException");
587         } catch (final IndexOutOfBoundsException ex) {
588         }
589 
590         try {
591             createCallSite(publicLookup, REMOVE_ELEMENT.named(list.size()), mt).getTarget().invoke(list);
592             throw new RuntimeException("expected IndexOutOfBoundsException");
593         } catch (final IndexOutOfBoundsException ex) {
594         }
595     }
596 
597     @Test(dataProvider = "flags")
removeElementFromMapTest(final boolean publicLookup)598     public void removeElementFromMapTest(final boolean publicLookup) throws Throwable {
599         final MethodType mt = MethodType.methodType(void.class, Object.class, Object.class);
600         final CallSite cs = createCallSite(publicLookup, REMOVE_ELEMENT, mt);
601 
602         final Map<String, String> map = new HashMap<>(Map.of("k1", "v1", "k2", "v2", "k3", "v3"));
603 
604         cs.getTarget().invoke(map, "k2");
605         Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3"));
606         cs.getTarget().invoke(map, "k4");
607         Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3"));
608         cs.getTarget().invoke(map, "k1");
609         Assert.assertEquals(map, Map.of("k3", "v3"));
610     }
611 
612 
613     @Test(dataProvider = "flags")
removeElementFromMapWithFixedKeyTest(final boolean publicLookup)614     public void removeElementFromMapWithFixedKeyTest(final boolean publicLookup) throws Throwable {
615         final MethodType mt = MethodType.methodType(void.class, Object.class);
616 
617         final Map<String, String> map = new HashMap<>(Map.of("k1", "v1", "k2", "v2", "k3", "v3"));
618 
619         createCallSite(publicLookup, REMOVE_ELEMENT.named("k2"), mt).getTarget().invoke(map);
620         Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3"));
621         createCallSite(publicLookup, REMOVE_ELEMENT.named("k4"), mt).getTarget().invoke(map);
622         Assert.assertEquals(map, Map.of("k1", "v1", "k3", "v3"));
623         createCallSite(publicLookup, REMOVE_ELEMENT.named("k1"), mt).getTarget().invoke(map);
624         Assert.assertEquals(map, Map.of("k3", "v3"));
625     }
626 }
627