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 annotations.ConstantMethodHandle; 18 import annotations.ConstantMethodType; 19 import java.lang.invoke.MethodHandle; 20 import java.lang.invoke.MethodType; 21 import java.lang.invoke.WrongMethodTypeException; 22 23 import java.io.StreamTokenizer; 24 import java.io.StringReader; 25 import java.util.Stack; 26 27 class Main { 28 /** 29 * Number of iterations run to attempt to trigger JIT compilation. These tests run on ART and 30 * the RI so they iterate rather than using the ART only native method ensureJitCompiled(). 31 */ 32 private static final int ITERATIONS_FOR_JIT = 12000; 33 34 /** A static field updated by method handle getters and setters. */ 35 private static String name = "default"; 36 unreachable()37 private static void unreachable() { 38 throw new Error("Unreachable"); 39 } 40 assertEquals(Object expected, Object actual)41 private static void assertEquals(Object expected, Object actual) { 42 if (!expected.equals(actual)) { 43 throw new AssertionError("Assertion failure: " + expected + " != " + actual); 44 } 45 } 46 47 private static class LocalClass { LocalClass()48 public LocalClass() {} 49 50 private int field; 51 } 52 53 private static class TestTokenizer extends StreamTokenizer { TestTokenizer(String message)54 public TestTokenizer(String message) { 55 super(new StringReader(message)); 56 } 57 } 58 59 @ConstantMethodType( 60 returnType = String.class, 61 parameterTypes = {int.class, Integer.class, System.class}) methodType0()62 private static MethodType methodType0() { 63 unreachable(); 64 return null; 65 } 66 67 @ConstantMethodType( 68 returnType = void.class, 69 parameterTypes = {LocalClass.class}) methodType1()70 private static MethodType methodType1() { 71 unreachable(); 72 return null; 73 } 74 75 @ConstantMethodType( 76 returnType = void.class, 77 parameterTypes = {MissingType.class}) missingType()78 private static MethodType missingType() { 79 unreachable(); 80 return null; 81 } 82 repeatConstMethodType0(MethodType expected)83 private static void repeatConstMethodType0(MethodType expected) { 84 System.out.print("repeatConstMethodType0("); 85 System.out.print(expected); 86 System.out.println(")"); 87 for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { 88 MethodType actual = methodType0(); 89 assertEquals(expected, actual); 90 } 91 } 92 repeatConstMethodType1(MethodType expected)93 private static void repeatConstMethodType1(MethodType expected) { 94 System.out.print("repeatConstMethodType1("); 95 System.out.print(expected); 96 System.out.println(")"); 97 for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { 98 MethodType actual = methodType1(); 99 assertEquals(expected, actual); 100 } 101 } 102 helloWorld(String who)103 static void helloWorld(String who) { 104 System.out.print("Hello World! And Hello "); 105 System.out.println(who); 106 } 107 108 @ConstantMethodHandle( 109 kind = ConstantMethodHandle.INVOKE_STATIC, 110 owner = "Main", 111 fieldOrMethodName = "helloWorld", 112 descriptor = "(Ljava/lang/String;)V") printHelloHandle()113 private static MethodHandle printHelloHandle() { 114 unreachable(); 115 return null; 116 } 117 118 @ConstantMethodHandle( 119 kind = ConstantMethodHandle.STATIC_PUT, 120 owner = "Main", 121 fieldOrMethodName = "name", 122 descriptor = "Ljava/lang/String;") setNameHandle()123 private static MethodHandle setNameHandle() { 124 unreachable(); 125 return null; 126 } 127 128 @ConstantMethodHandle( 129 kind = ConstantMethodHandle.STATIC_GET, 130 owner = "Main", 131 fieldOrMethodName = "name", 132 descriptor = "Ljava/lang/String;") getNameHandle()133 private static MethodHandle getNameHandle() { 134 unreachable(); 135 return null; 136 } 137 138 @ConstantMethodHandle( 139 kind = ConstantMethodHandle.STATIC_GET, 140 owner = "java/lang/Math", 141 fieldOrMethodName = "E", 142 descriptor = "D") getMathE()143 private static MethodHandle getMathE() { 144 unreachable(); 145 return null; 146 } 147 148 @ConstantMethodHandle( 149 kind = ConstantMethodHandle.STATIC_PUT, 150 owner = "java/lang/Math", 151 fieldOrMethodName = "E", 152 descriptor = "D") putMathE()153 private static MethodHandle putMathE() { 154 unreachable(); 155 return null; 156 } 157 158 @ConstantMethodHandle( 159 kind = ConstantMethodHandle.INSTANCE_GET, 160 owner = "java/io/StreamTokenizer", 161 fieldOrMethodName = "sval", 162 descriptor = "Ljava/lang/String;") getSval()163 private static MethodHandle getSval() { 164 unreachable(); 165 return null; 166 } 167 168 // This constant-method-handle references a private instance field. If 169 // referenced in bytecode it raises IAE at load time. 170 @ConstantMethodHandle( 171 kind = ConstantMethodHandle.INSTANCE_PUT, 172 owner = "java/io/StreamTokenizer", 173 fieldOrMethodName = "peekc", 174 descriptor = "I") putPeekc()175 private static MethodHandle putPeekc() { 176 unreachable(); 177 return null; 178 } 179 180 @ConstantMethodHandle( 181 kind = ConstantMethodHandle.INVOKE_VIRTUAL, 182 owner = "java/util/Stack", 183 fieldOrMethodName = "pop", 184 descriptor = "()Ljava/lang/Object;") stackPop()185 private static MethodHandle stackPop() { 186 unreachable(); 187 return null; 188 } 189 190 @ConstantMethodHandle( 191 kind = ConstantMethodHandle.INVOKE_VIRTUAL, 192 owner = "java/util/Stack", 193 fieldOrMethodName = "trimToSize", 194 descriptor = "()V") stackTrim()195 private static MethodHandle stackTrim() { 196 unreachable(); 197 return null; 198 } 199 200 @ConstantMethodHandle( 201 kind = ConstantMethodHandle.STATIC_GET, 202 owner = "PrivateMember", 203 fieldOrMethodName = "privateField", 204 descriptor = "I") getPrivateField()205 private static MethodHandle getPrivateField() { 206 unreachable(); 207 return null; 208 } 209 repeatConstMethodHandle()210 private static void repeatConstMethodHandle() throws Throwable { 211 System.out.println("repeatConstMethodHandle()"); 212 String[] values = {"A", "B", "C"}; 213 for (int i = 0; i < ITERATIONS_FOR_JIT; ++i) { 214 String value = values[i % values.length]; 215 setNameHandle().invoke(value); 216 String actual = (String) getNameHandle().invokeExact(); 217 assertEquals(value, actual); 218 assertEquals(value, name); 219 } 220 } 221 main(String[] args)222 public static void main(String[] args) throws Throwable { 223 System.out.println(methodType0()); 224 repeatConstMethodType0( 225 MethodType.methodType(String.class, int.class, Integer.class, System.class)); 226 repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class)); 227 printHelloHandle().invokeExact("Zog"); 228 printHelloHandle().invokeExact("Zorba"); 229 setNameHandle().invokeExact("HoverFly"); 230 System.out.print("name is "); 231 System.out.println(name); 232 System.out.println(getMathE().invoke()); 233 repeatConstMethodHandle(); 234 try { 235 putMathE().invokeExact(Math.PI); 236 unreachable(); 237 } catch (IllegalAccessError expected) { 238 System.out.println("Attempting to set Math.E raised IAE"); 239 } 240 241 StreamTokenizer st = new StreamTokenizer(new StringReader("Quack Moo Woof")); 242 while (st.nextToken() != StreamTokenizer.TT_EOF) { 243 System.out.println((String) getSval().invokeExact(st)); 244 } 245 246 TestTokenizer tt = new TestTokenizer("Test message 123"); 247 tt.nextToken(); 248 System.out.println((String) getSval().invoke(tt)); 249 try { 250 System.out.println((String) getSval().invokeExact(tt)); 251 } catch (WrongMethodTypeException wmte) { 252 System.out.println("Getting field in TestTokenizer raised WMTE (woohoo!)"); 253 } 254 255 Stack stack = new Stack(); 256 stack.push(Integer.valueOf(3)); 257 stack.push(Integer.valueOf(5)); 258 stack.push(Integer.valueOf(7)); 259 Object tos = stackPop().invokeExact(stack); 260 System.out.println("Stack: tos was " + tos); 261 System.out.println("Stack: capacity was " + stack.capacity()); 262 stackTrim().invokeExact(stack); 263 System.out.println("Stack: capacity is " + stack.capacity()); 264 265 // We used to not report in the compiler that loading a ConstMethodHandle/ConstMethodType 266 // can throw, which meant we were not catching the exception in the situation where we 267 // inline the loading. 268 try { 269 $inline$getPrivateField(); 270 System.out.println("Expected IllegalAccessError"); 271 } catch (IllegalAccessError e) { 272 // expected 273 } 274 275 try { 276 $inline$missingType(); 277 System.out.println("Expected NoClassDefFoundError"); 278 } catch (NoClassDefFoundError e) { 279 // expected 280 } 281 } 282 $inline$getPrivateField()283 public static void $inline$getPrivateField() { 284 getPrivateField(); 285 } 286 $inline$missingType()287 public static void $inline$missingType() { 288 missingType(); 289 } 290 } 291 292 class PrivateMember { 293 private static int privateField; 294 } 295 296 class MissingType { 297 } 298