• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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