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
  68     private static Object[][] flags() {
  69         return new Object[][]{
  70             {Boolean.FALSE},
  71             {Boolean.TRUE}
  72         };
  73     }
  74 
  75     // helpers to create callsite objects
  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 
  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 
  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 
  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 
 108     private static Object throwArrayIndexOutOfBounds(final Object receiver, final Object index) {
 109         throw new ArrayIndexOutOfBoundsException(String.valueOf(index));
 110     }
 111 
 112     private static Object throwIndexOutOfBounds(final Object receiver, final Object index) {
 113         throw new IndexOutOfBoundsException(String.valueOf(index));
 114     }
 115 
 116     @BeforeTest
 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
 173     public void afterTest() {
 174         this.linker = null;
 175     }
 176 
 177     @Test(dataProvider = "flags")
 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")
 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")
 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")
 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")
 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")
 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 
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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")
 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 }