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