ChildClass.java revision 9e68ade384abdb15714054feaed06cb38eb5432f
1/*
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
96    boolean isSameBoot = (isParentInBoot == isChildInBoot);
97
98    // Run meaningful combinations of access flags.
99    for (Hiddenness hiddenness : Hiddenness.values()) {
100      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 {
106        expected = Behaviour.Warning;
107      }
108
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
123          // Check whether one can use an interface default method.
124          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);
392    boolean setsWarning = (behaviour == Behaviour.Warning);
393
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
406  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}
439