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