blob: 582e907ca3d85fd1c7067433698c9e3c330f9afa [file] [log] [blame]
David Brazdil11b67b22018-01-18 16:41:40 +00001/*
2 * Copyright (C) 2017 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
17import java.lang.annotation.Annotation;
18import java.lang.reflect.Constructor;
19import java.lang.reflect.Field;
20import java.lang.reflect.InvocationTargetException;
21import java.lang.reflect.Method;
22import java.util.Arrays;
23import java.util.List;
24
25import javax.lang.model.element.AnnotationMirror;
26import javax.lang.model.type.PrimitiveType;
27import javax.lang.model.type.TypeKind;
28import javax.lang.model.type.TypeVisitor;
29
30public class ChildClass {
31 enum PrimitiveType {
32 TInteger('I', Integer.TYPE, Integer.valueOf(0)),
33 TLong('J', Long.TYPE, Long.valueOf(0)),
34 TFloat('F', Float.TYPE, Float.valueOf(0)),
35 TDouble('D', Double.TYPE, Double.valueOf(0)),
36 TBoolean('Z', Boolean.TYPE, Boolean.valueOf(false)),
37 TByte('B', Byte.TYPE, Byte.valueOf((byte) 0)),
38 TShort('S', Short.TYPE, Short.valueOf((short) 0)),
39 TCharacter('C', Character.TYPE, Character.valueOf('0'));
40
41 PrimitiveType(char shorty, Class klass, Object value) {
42 mShorty = shorty;
43 mClass = klass;
44 mDefaultValue = value;
45 }
46
47 public char mShorty;
48 public Class mClass;
49 public Object mDefaultValue;
50 }
51
52 enum Hiddenness {
53 Whitelist(PrimitiveType.TShort),
54 LightGreylist(PrimitiveType.TBoolean),
55 DarkGreylist(PrimitiveType.TByte),
56 Blacklist(PrimitiveType.TCharacter);
57
58 Hiddenness(PrimitiveType type) { mAssociatedType = type; }
59 public PrimitiveType mAssociatedType;
60 }
61
62 enum Visibility {
63 Public(PrimitiveType.TInteger),
64 Package(PrimitiveType.TFloat),
65 Protected(PrimitiveType.TLong),
66 Private(PrimitiveType.TDouble);
67
68 Visibility(PrimitiveType type) { mAssociatedType = type; }
69 public PrimitiveType mAssociatedType;
70 }
71
72 enum Behaviour {
73 Granted,
74 Warning,
75 Denied,
76 }
77
78 private static final boolean booleanValues[] = new boolean[] { false, true };
79
80 public static void runTest(String libFileName, boolean expectedParentInBoot,
81 boolean expectedChildInBoot) throws Exception {
82 System.load(libFileName);
83
84 // Check expectations about loading into boot class path.
85 isParentInBoot = (ParentClass.class.getClassLoader().getParent() == null);
86 if (isParentInBoot != expectedParentInBoot) {
87 throw new RuntimeException("Expected ParentClass " +
88 (expectedParentInBoot ? "" : "not ") + "in boot class path");
89 }
90 isChildInBoot = (ChildClass.class.getClassLoader().getParent() == null);
91 if (isChildInBoot != expectedChildInBoot) {
92 throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
93 "in boot class path");
94 }
95
David Brazdil5a61bb72018-01-19 16:59:46 +000096 boolean isSameBoot = (isParentInBoot == isChildInBoot);
97
David Brazdil11b67b22018-01-18 16:41:40 +000098 // Run meaningful combinations of access flags.
99 for (Hiddenness hiddenness : Hiddenness.values()) {
David Brazdil5a61bb72018-01-19 16:59:46 +0000100 final Behaviour expected;
101 if (isSameBoot || hiddenness == Hiddenness.Whitelist) {
102 expected = Behaviour.Granted;
103 } else if (hiddenness == Hiddenness.Blacklist) {
104 expected = Behaviour.Denied;
105 } else {
David Brazdilee7d2fd2018-01-20 17:25:23 +0000106 expected = Behaviour.Warning;
David Brazdil5a61bb72018-01-19 16:59:46 +0000107 }
David Brazdil11b67b22018-01-18 16:41:40 +0000108
109 for (boolean isStatic : booleanValues) {
110 String suffix = (isStatic ? "Static" : "") + hiddenness.name();
111
112 for (Visibility visibility : Visibility.values()) {
113 // Test reflection and JNI on methods and fields
114 for (Class klass : new Class<?>[] { ParentClass.class, ParentInterface.class }) {
115 String baseName = visibility.name() + suffix;
116 checkField(klass, "field" + baseName, isStatic, visibility, expected);
117 checkMethod(klass, "method" + baseName, isStatic, visibility, expected);
118 }
119
120 // Check whether one can use a class constructor.
121 checkConstructor(ParentClass.class, visibility, hiddenness, expected);
122
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000123 // Check whether one can use an interface default method.
David Brazdil11b67b22018-01-18 16:41:40 +0000124 String name = "method" + visibility.name() + "Default" + hiddenness.name();
125 checkMethod(ParentInterface.class, name, /*isStatic*/ false, visibility, expected);
126 }
127
128 // Test whether static linking succeeds.
129 checkLinking("LinkFieldGet" + suffix, /*takesParameter*/ false, expected);
130 checkLinking("LinkFieldSet" + suffix, /*takesParameter*/ true, expected);
131 checkLinking("LinkMethod" + suffix, /*takesParameter*/ false, expected);
132 }
133
134 // Check whether Class.newInstance succeeds.
135 checkNullaryConstructor(Class.forName("NullaryConstructor" + hiddenness.name()), expected);
136 }
137 }
138
139 private static void checkField(Class<?> klass, String name, boolean isStatic,
140 Visibility visibility, Behaviour behaviour) throws Exception {
141
142 boolean isPublic = (visibility == Visibility.Public);
143 boolean canDiscover = (behaviour != Behaviour.Denied);
144 boolean setsWarning = (behaviour == Behaviour.Warning);
145
146 if (klass.isInterface() && (!isStatic || !isPublic)) {
147 // Interfaces only have public static fields.
148 return;
149 }
150
151 // Test discovery with reflection.
152
153 if (Reflection.canDiscoverWithGetDeclaredField(klass, name) != canDiscover) {
154 throwDiscoveryException(klass, name, true, "getDeclaredField()", canDiscover);
155 }
156
157 if (Reflection.canDiscoverWithGetDeclaredFields(klass, name) != canDiscover) {
158 throwDiscoveryException(klass, name, true, "getDeclaredFields()", canDiscover);
159 }
160
161 if (Reflection.canDiscoverWithGetField(klass, name) != (canDiscover && isPublic)) {
162 throwDiscoveryException(klass, name, true, "getField()", (canDiscover && isPublic));
163 }
164
165 if (Reflection.canDiscoverWithGetFields(klass, name) != (canDiscover && isPublic)) {
166 throwDiscoveryException(klass, name, true, "getFields()", (canDiscover && isPublic));
167 }
168
169 // Test discovery with JNI.
170
171 if (JNI.canDiscoverField(klass, name, isStatic) != canDiscover) {
172 throwDiscoveryException(klass, name, true, "JNI", canDiscover);
173 }
174
175 // Finish here if we could not discover the field.
176
177 if (!canDiscover) {
178 return;
179 }
180
181 // Test that modifiers are unaffected.
182
183 if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) {
184 throwModifiersException(klass, name, true);
185 }
186
187 // Test getters and setters when meaningful.
188
189 clearWarning();
190 if (!Reflection.canGetField(klass, name)) {
191 throwAccessException(klass, name, true, "Field.getInt()");
192 }
193 if (hasPendingWarning() != setsWarning) {
194 throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
195 }
196
197 clearWarning();
198 if (!Reflection.canSetField(klass, name)) {
199 throwAccessException(klass, name, true, "Field.setInt()");
200 }
201 if (hasPendingWarning() != setsWarning) {
202 throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
203 }
204
205 clearWarning();
206 if (!JNI.canGetField(klass, name, isStatic)) {
207 throwAccessException(klass, name, true, "getIntField");
208 }
209 if (hasPendingWarning() != setsWarning) {
210 throwWarningException(klass, name, true, "getIntField", setsWarning);
211 }
212
213 clearWarning();
214 if (!JNI.canSetField(klass, name, isStatic)) {
215 throwAccessException(klass, name, true, "setIntField");
216 }
217 if (hasPendingWarning() != setsWarning) {
218 throwWarningException(klass, name, true, "setIntField", setsWarning);
219 }
220 }
221
222 private static void checkMethod(Class<?> klass, String name, boolean isStatic,
223 Visibility visibility, Behaviour behaviour) throws Exception {
224
225 boolean isPublic = (visibility == Visibility.Public);
226 if (klass.isInterface() && !isPublic) {
227 // All interface members are public.
228 return;
229 }
230
231 boolean canDiscover = (behaviour != Behaviour.Denied);
232 boolean setsWarning = (behaviour == Behaviour.Warning);
233
234 // Test discovery with reflection.
235
236 if (Reflection.canDiscoverWithGetDeclaredMethod(klass, name) != canDiscover) {
237 throwDiscoveryException(klass, name, false, "getDeclaredMethod()", canDiscover);
238 }
239
240 if (Reflection.canDiscoverWithGetDeclaredMethods(klass, name) != canDiscover) {
241 throwDiscoveryException(klass, name, false, "getDeclaredMethods()", canDiscover);
242 }
243
244 if (Reflection.canDiscoverWithGetMethod(klass, name) != (canDiscover && isPublic)) {
245 throwDiscoveryException(klass, name, false, "getMethod()", (canDiscover && isPublic));
246 }
247
248 if (Reflection.canDiscoverWithGetMethods(klass, name) != (canDiscover && isPublic)) {
249 throwDiscoveryException(klass, name, false, "getMethods()", (canDiscover && isPublic));
250 }
251
252 // Test discovery with JNI.
253
254 if (JNI.canDiscoverMethod(klass, name, isStatic) != canDiscover) {
255 throwDiscoveryException(klass, name, false, "JNI", canDiscover);
256 }
257
258 // Finish here if we could not discover the field.
259
260 if (!canDiscover) {
261 return;
262 }
263
264 // Test that modifiers are unaffected.
265
266 if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) {
267 throwModifiersException(klass, name, false);
268 }
269
270 // Test whether we can invoke the method. This skips non-static interface methods.
271
272 if (!klass.isInterface() || isStatic) {
273 clearWarning();
274 if (!Reflection.canInvokeMethod(klass, name)) {
275 throwAccessException(klass, name, false, "invoke()");
276 }
277 if (hasPendingWarning() != setsWarning) {
278 throwWarningException(klass, name, false, "invoke()", setsWarning);
279 }
280
281 clearWarning();
282 if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
283 throwAccessException(klass, name, false, "CallMethodA");
284 }
285 if (hasPendingWarning() != setsWarning) {
286 throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
287 }
288
289 clearWarning();
290 if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
291 throwAccessException(klass, name, false, "CallMethodV");
292 }
293 if (hasPendingWarning() != setsWarning) {
294 throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
295 }
296 }
297 }
298
299 private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
300 Behaviour behaviour) throws Exception {
301
302 boolean isPublic = (visibility == Visibility.Public);
303 String signature = "(" + visibility.mAssociatedType.mShorty +
304 hiddenness.mAssociatedType.mShorty + ")V";
305 String fullName = "<init>" + signature;
306 Class<?> args[] = new Class[] { visibility.mAssociatedType.mClass,
307 hiddenness.mAssociatedType.mClass };
308 Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue,
309 hiddenness.mAssociatedType.mDefaultValue };
310
311 boolean canDiscover = (behaviour != Behaviour.Denied);
312 boolean setsWarning = (behaviour == Behaviour.Warning);
313
314 // Test discovery with reflection.
315
316 if (Reflection.canDiscoverWithGetDeclaredConstructor(klass, args) != canDiscover) {
317 throwDiscoveryException(klass, fullName, false, "getDeclaredConstructor()", canDiscover);
318 }
319
320 if (Reflection.canDiscoverWithGetDeclaredConstructors(klass, args) != canDiscover) {
321 throwDiscoveryException(klass, fullName, false, "getDeclaredConstructors()", canDiscover);
322 }
323
324 if (Reflection.canDiscoverWithGetConstructor(klass, args) != (canDiscover && isPublic)) {
325 throwDiscoveryException(
326 klass, fullName, false, "getConstructor()", (canDiscover && isPublic));
327 }
328
329 if (Reflection.canDiscoverWithGetConstructors(klass, args) != (canDiscover && isPublic)) {
330 throwDiscoveryException(
331 klass, fullName, false, "getConstructors()", (canDiscover && isPublic));
332 }
333
334 // Test discovery with JNI.
335
336 if (JNI.canDiscoverConstructor(klass, signature) != canDiscover) {
337 throwDiscoveryException(klass, fullName, false, "JNI", canDiscover);
338 }
339
340 // Finish here if we could not discover the field.
341
342 if (!canDiscover) {
343 return;
344 }
345
346 // Test whether we can invoke the constructor.
347
348 clearWarning();
349 if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
350 throwAccessException(klass, fullName, false, "invoke()");
351 }
352 if (hasPendingWarning() != setsWarning) {
353 throwWarningException(klass, fullName, false, "invoke()", setsWarning);
354 }
355
356 clearWarning();
357 if (!JNI.canInvokeConstructorA(klass, signature)) {
358 throwAccessException(klass, fullName, false, "NewObjectA");
359 }
360 if (hasPendingWarning() != setsWarning) {
361 throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
362 }
363
364 clearWarning();
365 if (!JNI.canInvokeConstructorV(klass, signature)) {
366 throwAccessException(klass, fullName, false, "NewObjectV");
367 }
368 if (hasPendingWarning() != setsWarning) {
369 throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
370 }
371 }
372
373 private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
374 throws Exception {
375 boolean canAccess = (behaviour != Behaviour.Denied);
376 boolean setsWarning = (behaviour == Behaviour.Warning);
377
378 clearWarning();
379 if (Reflection.canUseNewInstance(klass) != canAccess) {
380 throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
381 "be able to construct " + klass.getName() + ". " +
382 "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
383 }
384 if (canAccess && hasPendingWarning() != setsWarning) {
385 throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
386 }
387 }
388
389 private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
390 throws Exception {
391 boolean canAccess = (behaviour != Behaviour.Denied);
David Brazdil8ce3bfa2018-03-12 18:01:18 +0000392 boolean setsWarning = (behaviour == Behaviour.Warning);
David Brazdil11b67b22018-01-18 16:41:40 +0000393
394 clearWarning();
395 if (Linking.canAccess(className, takesParameter) != canAccess) {
396 throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
397 "be able to verify " + className + "." +
398 "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
399 }
400 if (canAccess && hasPendingWarning() != setsWarning) {
401 throwWarningException(
402 Class.forName(className), "access", false, "static linking", setsWarning);
403 }
404 }
405
David Brazdil11b67b22018-01-18 16:41:40 +0000406 private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
407 String fn, boolean canAccess) {
408 throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
409 "." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
410 "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
411 }
412
413 private static void throwAccessException(Class<?> klass, String name, boolean isField,
414 String fn) {
415 throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
416 klass.getName() + "." + name + " using " + fn + ". " +
417 "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
418 }
419
420 private static void throwWarningException(Class<?> klass, String name, boolean isField,
421 String fn, boolean setsWarning) {
422 throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
423 klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
424 "set the warning flag. " +
425 "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
426 }
427
428 private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
429 throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
430 "." + name + " to not expose hidden modifiers");
431 }
432
433 private static boolean isParentInBoot;
434 private static boolean isChildInBoot;
435
436 private static native boolean hasPendingWarning();
437 private static native void clearWarning();
438}