Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 import java.lang.invoke.MethodHandle;
     18 import java.lang.invoke.MethodHandles;
     19 import java.lang.invoke.MethodType;
     20 import java.lang.invoke.VarHandle;
     21 import java.lang.invoke.WrongMethodTypeException;
     22 
     23 public final class Main {
     24     static class TestSetupError extends Error {
     25         TestSetupError(String message, Throwable cause) {
     26             super(message, cause);
     27         }
     28     }
     29 
     30     private static void failAssertion(String message) {
     31         StringBuilder sb = new StringBuilder();
     32         sb.append("Test failure: ");
     33         sb.append(message);
     34         throw new AssertionError(sb.toString());
     35     }
     36 
     37     private static void assertUnreachable() throws Throwable {
     38         failAssertion("Unreachable");
     39     }
     40 
     41     private static void failAssertEquals(Object expected, Object actual) {
     42         StringBuilder sb = new StringBuilder();
     43         sb.append(expected);
     44         sb.append(" != ");
     45         sb.append(actual);
     46         failAssertion(sb.toString());
     47     }
     48 
     49     private static void assertEquals(int expected, int actual) {
     50         if (expected != actual) {
     51             failAssertEquals(expected, actual);
     52         }
     53     }
     54 
     55     private static void assertEquals(float expected, float actual) {
     56         if (expected != actual) {
     57             failAssertEquals(expected, actual);
     58         }
     59     }
     60 
     61     private static void assertEquals(double expected, double actual) {
     62         if (expected != actual) {
     63             failAssertEquals(expected, actual);
     64         }
     65     }
     66 
     67     static class FieldVarHandleExactInvokerTest {
     68         private static final VarHandle fieldVarHandle;
     69         int field;
     70 
     71         static {
     72             try {
     73                 fieldVarHandle =
     74                         MethodHandles.lookup()
     75                                 .findVarHandle(
     76                                         FieldVarHandleExactInvokerTest.class, "field", int.class);
     77             } catch (Exception e) {
     78                 throw new TestSetupError("Failed to lookup of field", e);
     79             }
     80         }
     81 
     82         void run() throws Throwable {
     83             System.out.println("fieldVarHandleExactInvokerTest");
     84 
     85             MethodHandle invokerMethodHandle =
     86                     MethodHandles.varHandleExactInvoker(
     87                             VarHandle.AccessMode.GET_AND_SET,
     88                             MethodType.methodType(
     89                                     int.class, FieldVarHandleExactInvokerTest.class, int.class));
     90 
     91             field = 3;
     92             assertEquals(3, (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 4));
     93             assertEquals(4, field);
     94 
     95             //
     96             // Check invocations with MethodHandle.invokeExact()
     97             //
     98             try {
     99                 // Check for unboxing
    100                 int i =
    101                         (int)
    102                                 invokerMethodHandle.invokeExact(
    103                                         fieldVarHandle, this, Integer.valueOf(3));
    104                 assertUnreachable();
    105             } catch (WrongMethodTypeException expected) {
    106                 assertEquals(4, field);
    107             }
    108             try {
    109                 // Check for widening conversion
    110                 int i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 3);
    111                 assertUnreachable();
    112             } catch (WrongMethodTypeException expected) {
    113                 assertEquals(4, field);
    114             }
    115             try {
    116                 // Check for acceptance of void return type
    117                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
    118                 assertUnreachable();
    119             } catch (WrongMethodTypeException expected) {
    120                 assertEquals(4, field);
    121             }
    122             try {
    123                 // Check for wider return type
    124                 long l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
    125                 assertUnreachable();
    126             } catch (WrongMethodTypeException expected) {
    127                 assertEquals(4, field);
    128             }
    129             try {
    130                 // Check null VarHandle instance fails
    131                 VarHandle vhNull = null;
    132                 int i = (int) invokerMethodHandle.invokeExact(vhNull, this, 777);
    133                 assertUnreachable();
    134             } catch (NullPointerException expected) {
    135                 assertEquals(4, field);
    136             }
    137 
    138             //
    139             // Check invocations with MethodHandle.invoke()
    140             //
    141 
    142             // Check for unboxing
    143             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
    144             assertEquals(3, field);
    145 
    146             // Check for unboxing
    147             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Short.valueOf((short) 4));
    148             assertEquals(4, field);
    149 
    150             // Check for widening conversion
    151             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 23);
    152             assertEquals(23, field);
    153 
    154             // Check for acceptance of void return type
    155             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
    156             assertEquals(77, field);
    157 
    158             // Check for wider return type
    159             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
    160             assertEquals(88, field);
    161 
    162             try {
    163                 // Check null VarHandle instance fails
    164                 VarHandle vhNull = null;
    165                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
    166                 assertUnreachable();
    167             } catch (NullPointerException expected) {
    168                 assertEquals(88, field);
    169             }
    170         }
    171     }
    172 
    173     static class FieldVarHandleInvokerTest {
    174         private static final VarHandle fieldVarHandle;
    175         int field;
    176 
    177         static {
    178             try {
    179                 fieldVarHandle =
    180                         MethodHandles.lookup()
    181                                 .findVarHandle(FieldVarHandleInvokerTest.class, "field", int.class);
    182             } catch (Exception e) {
    183                 throw new TestSetupError("Failed to lookup of field", e);
    184             }
    185         }
    186 
    187         void run() throws Throwable {
    188             System.out.println("fieldVarHandleInvokerTest");
    189             MethodHandle invokerMethodHandle =
    190                     MethodHandles.varHandleInvoker(
    191                             VarHandle.AccessMode.GET_AND_SET,
    192                             MethodType.methodType(
    193                                     int.class, FieldVarHandleInvokerTest.class, int.class));
    194 
    195             field = 3;
    196             int oldField = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 4);
    197             assertEquals(3, oldField);
    198             assertEquals(4, field);
    199 
    200             //
    201             // Check invocations with MethodHandle.invoke()
    202             //
    203 
    204             // Check for unboxing
    205             int i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Integer.valueOf(3));
    206             assertEquals(3, field);
    207 
    208             // Check for widening conversion
    209             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, (short) 33);
    210             assertEquals(33, field);
    211 
    212             // Check for widening conversion
    213             i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, Byte.valueOf((byte) 34));
    214             assertEquals(34, field);
    215 
    216             // Check for acceptance of void return type
    217             invokerMethodHandle.invoke(fieldVarHandle, this, 77);
    218             assertEquals(77, field);
    219 
    220             // Check for wider return type
    221             long l = (long) invokerMethodHandle.invoke(fieldVarHandle, this, 88);
    222             assertEquals(88, field);
    223             try {
    224                 // Check narrowing conversion fails
    225                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, 3.0);
    226                 assertUnreachable();
    227             } catch (WrongMethodTypeException expected) {
    228             }
    229             try {
    230                 // Check reference type fails
    231                 i = (int) invokerMethodHandle.invoke(fieldVarHandle, this, "Bad");
    232                 assertUnreachable();
    233             } catch (WrongMethodTypeException expected) {
    234             }
    235             try {
    236                 // Check null VarHandle instance fails
    237                 VarHandle vhNull = null;
    238                 i = (int) invokerMethodHandle.invoke(vhNull, this, 888);
    239                 assertUnreachable();
    240             } catch (NullPointerException expected) {
    241                 assertEquals(88, field);
    242             }
    243 
    244             //
    245             // Check invocations with MethodHandle.invokeExact()
    246             //
    247             field = -1;
    248             try {
    249                 // Check for unboxing
    250                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, Integer.valueOf(3));
    251                 assertUnreachable();
    252             } catch (WrongMethodTypeException expected) {
    253                 assertEquals(-1, field);
    254             }
    255             try {
    256                 // Check for widening conversion
    257                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, (short) 33);
    258                 assertUnreachable();
    259             } catch (WrongMethodTypeException expected) {
    260                 assertEquals(-1, field);
    261             }
    262             try {
    263                 // Check for acceptance of void return type
    264                 invokerMethodHandle.invokeExact(fieldVarHandle, this, 77);
    265                 assertUnreachable();
    266             } catch (WrongMethodTypeException expected) {
    267                 assertEquals(-1, field);
    268             }
    269             try {
    270                 // Check for wider return type
    271                 l = (long) invokerMethodHandle.invokeExact(fieldVarHandle, this, 78);
    272                 assertUnreachable();
    273             } catch (WrongMethodTypeException expected) {
    274                 assertEquals(-1, field);
    275             }
    276             try {
    277                 // Check narrowing conversion fails
    278                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, 3.0);
    279                 assertUnreachable();
    280             } catch (WrongMethodTypeException expected) {
    281             }
    282             try {
    283                 // Check reference type fails
    284                 i = (int) invokerMethodHandle.invokeExact(fieldVarHandle, this, "Bad");
    285                 assertUnreachable();
    286             } catch (WrongMethodTypeException expected) {
    287             }
    288             try {
    289                 // Check null VarHandle instance fails
    290                 VarHandle vhNull = null;
    291                 i = (int) invokerMethodHandle.invokeExact(vhNull, this, 888);
    292                 assertUnreachable();
    293             } catch (NullPointerException expected) {
    294                 assertEquals(-1, field);
    295             }
    296         }
    297     }
    298 
    299     static class DivergenceExactInvokerTest {
    300         private static final VarHandle floatsArrayVarHandle;
    301 
    302         static {
    303             try {
    304                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
    305             } catch (Exception e) {
    306                 throw new TestSetupError("Failed to create VarHandle", e);
    307             }
    308         }
    309 
    310         void run() throws Throwable {
    311             System.out.println("DivergenceExactInvokerTest");
    312             float[] floatsArray = new float[4];
    313             // Exact invoker of an accessor having the form:
    314             //  float accessor(float[] values, int index, Float current, float replacement)
    315             MethodHandle exactInvoker =
    316                     MethodHandles.varHandleExactInvoker(
    317                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
    318                             MethodType.methodType(
    319                                     float.class,
    320                                     float[].class,
    321                                     int.class,
    322                                     Float.class,
    323                                     float.class));
    324             floatsArray[2] = Float.valueOf(4.0f);
    325             // Callsite that is an exact match with exactInvoker.type().
    326             try {
    327                 // exactInvoker.type() is not compatible with floatsArrayVarHandle accessor.
    328                 float old =
    329                         (float)
    330                                 exactInvoker.invoke(
    331                                         floatsArrayVarHandle,
    332                                         floatsArray,
    333                                         2,
    334                                         Float.valueOf(4.0f),
    335                                         8.0f);
    336                 assertUnreachable();
    337             } catch (WrongMethodTypeException expected) {
    338                 assertEquals(4.0f, floatsArray[2]);
    339             }
    340 
    341             // Callsites that are exact matches with exactInvoker.type()
    342             try {
    343                 // Mismatch between exactInvoker.type() and VarHandle type (Float != float)
    344                 float old =
    345                         (float)
    346                                 exactInvoker.invoke(
    347                                         floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
    348                 assertUnreachable();
    349             } catch (WrongMethodTypeException expected) {
    350                 assertEquals(4.0f, floatsArray[2]);
    351             }
    352             try {
    353                 // short not convertible to Float
    354                 float old =
    355                         (float)
    356                                 exactInvoker.invoke(
    357                                         floatsArrayVarHandle, floatsArray, 2, (short) 4, 13.0f);
    358                 assertUnreachable();
    359             } catch (WrongMethodTypeException expected) {
    360                 assertEquals(4.0f, floatsArray[2]);
    361             }
    362             try {
    363                 // int not convertible to Float
    364                 float old =
    365                         (float) exactInvoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
    366                 assertUnreachable();
    367             } catch (WrongMethodTypeException expected) {
    368                 assertEquals(4.0f, floatsArray[2]);
    369             }
    370         }
    371     }
    372 
    373     static class DivergenceInvokerTest {
    374         private static final VarHandle floatsArrayVarHandle;
    375 
    376         static {
    377             try {
    378                 floatsArrayVarHandle = MethodHandles.arrayElementVarHandle(float[].class);
    379             } catch (Exception e) {
    380                 throw new TestSetupError("Failed to create VarHandle", e);
    381             }
    382         }
    383 
    384         void run() throws Throwable {
    385             System.out.println("DivergenceInvokerTest");
    386             float[] floatsArray = new float[4];
    387             // Invoker of an accessor having the form:
    388             //  float accessor(float[] values, int index, Float current, float replacement)
    389             MethodHandle invoker =
    390                     MethodHandles.varHandleInvoker(
    391                             VarHandle.AccessMode.COMPARE_AND_EXCHANGE,
    392                             MethodType.methodType(
    393                                     float.class,
    394                                     float[].class,
    395                                     int.class,
    396                                     Float.class,
    397                                     float.class));
    398             floatsArray[2] = Float.valueOf(4.0f);
    399             // Callsite that is an exact match with invoker.type()
    400             float old =
    401                     (float)
    402                             invoker.invoke(
    403                                     floatsArrayVarHandle,
    404                                     floatsArray,
    405                                     2,
    406                                     Float.valueOf(4.0f),
    407                                     8.0f);
    408             assertEquals(4.0f, old);
    409             assertEquals(8.0f, floatsArray[2]);
    410 
    411             // Callsite that is convertible match to invoker.type()
    412             old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8.0f, 16.0f);
    413             assertEquals(8.0f, old);
    414             assertEquals(16.0f, floatsArray[2]);
    415 
    416             // Callsites that are not convertible to invoker.type().
    417             try {
    418                 // short is not convertible to Float
    419                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, (short) 4, 8.0f);
    420                 assertUnreachable();
    421             } catch (WrongMethodTypeException expected) {
    422                 assertEquals(16.0f, floatsArray[2]);
    423             }
    424             try {
    425                 // int is not convertible to Float
    426                 old = (float) invoker.invoke(floatsArrayVarHandle, floatsArray, 2, 8, -8.0f);
    427                 assertUnreachable();
    428             } catch (WrongMethodTypeException expected) {
    429                 assertEquals(16.0f, floatsArray[2]);
    430             }
    431         }
    432     }
    433 
    434     public static void main(String[] args) throws Throwable {
    435         new FieldVarHandleExactInvokerTest().run();
    436         new FieldVarHandleInvokerTest().run();
    437         new DivergenceExactInvokerTest().run();
    438         new DivergenceInvokerTest().run();
    439     }
    440 }
    441