1 /* 2 * Copyright (C) 2008 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 java.lang.reflect.Constructor; 18 import java.lang.reflect.Method; 19 20 /** 21 * Class loader test. 22 */ 23 public class Main { 24 /** 25 * Main entry point. 26 */ main(String[] args)27 public static void main(String[] args) throws Exception { 28 FancyLoader loader; 29 30 loader = new FancyLoader(ClassLoader.getSystemClassLoader()); 31 //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader()); 32 //System.out.println("ALTERN: " + loader); 33 34 /* 35 * This statement has no effect on this program, but it can 36 * change the point where a LinkageException is thrown in 37 * testImplement(). When this is present the "reference 38 * implementation" throws an exception from Class.newInstance(), 39 * when it's absent the exception is deferred until the first time 40 * we call a method that isn't actually implemented. 41 * 42 * This isn't the class that fails -- it's a class with the same 43 * name in the "fancy" class loader -- but the VM thinks it has a 44 * reference to one of these; presumably the difference is that 45 * without this the VM finds itself holding a reference to an 46 * instance of an uninitialized class. 47 */ 48 System.out.println("base: " + DoubledImplement.class); 49 System.out.println("base2: " + DoubledImplement2.class); 50 51 /* 52 * Run tests. 53 */ 54 testAccess1(loader); 55 testAccess2(loader); 56 testAccess3(loader); 57 58 testExtend(loader); 59 testExtendOkay(loader); 60 testInterface(loader); 61 testAbstract(loader); 62 testImplement(loader); 63 testIfaceImplement(loader); 64 65 testSeparation(); 66 67 testClassForName(); 68 69 testNullClassLoader(); 70 } 71 testNullClassLoader()72 static void testNullClassLoader() { 73 try { 74 /* this is the "alternate" DEX/Jar file */ 75 String DEX_FILE = System.getenv("DEX_LOCATION") + "/068-classloader-ex.jar"; 76 /* on Dalvik, this is a DexFile; otherwise, it's null */ 77 Class<?> mDexClass = Class.forName("dalvik.system.DexFile"); 78 Constructor<?> ctor = mDexClass.getConstructor(String.class); 79 Object mDexFile = ctor.newInstance(DEX_FILE); 80 Method meth = mDexClass.getMethod("loadClass", String.class, ClassLoader.class); 81 Object klass = meth.invoke(mDexFile, "Mutator", null); 82 if (klass == null) { 83 throw new AssertionError("loadClass with nullclass loader failed"); 84 } 85 } catch (Exception e) { 86 System.out.println(e); 87 } 88 System.out.println("Loaded class into null class loader"); 89 } 90 testSeparation()91 static void testSeparation() { 92 FancyLoader loader1 = new FancyLoader(ClassLoader.getSystemClassLoader()); 93 FancyLoader loader2 = new FancyLoader(ClassLoader.getSystemClassLoader()); 94 95 try { 96 Class<?> target1 = loader1.loadClass("MutationTarget"); 97 Class<?> target2 = loader2.loadClass("MutationTarget"); 98 99 if (target1 == target2) { 100 throw new RuntimeException("target1 should not be equal to target2"); 101 } 102 103 Class<?> mutator1 = loader1.loadClass("Mutator"); 104 Class<?> mutator2 = loader2.loadClass("Mutator"); 105 106 if (mutator1 == mutator2) { 107 throw new RuntimeException("mutator1 should not be equal to mutator2"); 108 } 109 110 runMutator(mutator1, 1); 111 112 int value = getMutationTargetValue(target1); 113 if (value != 1) { 114 throw new RuntimeException("target 1 has unexpected value " + value); 115 } 116 value = getMutationTargetValue(target2); 117 if (value != 0) { 118 throw new RuntimeException("target 2 has unexpected value " + value); 119 } 120 121 runMutator(mutator2, 2); 122 123 value = getMutationTargetValue(target1); 124 if (value != 1) { 125 throw new RuntimeException("target 1 has unexpected value " + value); 126 } 127 value = getMutationTargetValue(target2); 128 if (value != 2) { 129 throw new RuntimeException("target 2 has unexpected value " + value); 130 } 131 } catch (Exception ex) { 132 ex.printStackTrace(System.out); 133 } 134 } 135 runMutator(Class<?> c, int v)136 private static void runMutator(Class<?> c, int v) throws Exception { 137 java.lang.reflect.Method m = c.getDeclaredMethod("mutate", int.class); 138 m.invoke(null, v); 139 } 140 getMutationTargetValue(Class<?> c)141 private static int getMutationTargetValue(Class<?> c) throws Exception { 142 java.lang.reflect.Field f = c.getDeclaredField("value"); 143 return f.getInt(null); 144 } 145 146 /** 147 * See if we can load a class that isn't public to us. We should be 148 * able to load it but not instantiate it. 149 */ testAccess1(ClassLoader loader)150 static void testAccess1(ClassLoader loader) { 151 Class<?> altClass; 152 153 try { 154 altClass = loader.loadClass("Inaccessible1"); 155 } catch (ClassNotFoundException cnfe) { 156 System.out.println("loadClass failed"); 157 cnfe.printStackTrace(System.out); 158 return; 159 } 160 161 /* instantiate */ 162 Object obj; 163 try { 164 obj = altClass.newInstance(); 165 System.out.println("ERROR: Inaccessible1 was accessible"); 166 } catch (InstantiationException ie) { 167 System.out.println("newInstance failed: " + ie); 168 return; 169 } catch (IllegalAccessException iae) { 170 System.out.println("Got expected access exception #1"); 171 //System.out.println("+++ " + iae); 172 return; 173 } 174 } 175 176 /** 177 * See if we can load a class whose base class is not accessible to it 178 * (though the base *is* accessible to us). 179 */ testAccess2(ClassLoader loader)180 static void testAccess2(ClassLoader loader) { 181 Class<?> altClass; 182 183 try { 184 altClass = loader.loadClass("Inaccessible2"); 185 System.out.println("ERROR: Inaccessible2 was accessible: " + altClass); 186 } catch (ClassNotFoundException cnfe) { 187 Throwable cause = cnfe.getCause(); 188 if (cause instanceof IllegalAccessError) { 189 System.out.println("Got expected CNFE/IAE #2"); 190 } else { 191 System.out.println("Got unexpected CNFE/IAE #2"); 192 cnfe.printStackTrace(System.out); 193 } 194 } 195 } 196 197 /** 198 * See if we can load a class with an inaccessible interface. 199 */ testAccess3(ClassLoader loader)200 static void testAccess3(ClassLoader loader) { 201 Class<?> altClass; 202 203 try { 204 altClass = loader.loadClass("Inaccessible3"); 205 System.out.println("ERROR: Inaccessible3 was accessible: " + altClass); 206 } catch (ClassNotFoundException cnfe) { 207 Throwable cause = cnfe.getCause(); 208 if (cause instanceof IllegalAccessError) { 209 System.out.println("Got expected CNFE/IAE #3"); 210 } else { 211 System.out.println("Got unexpected CNFE/IAE #3"); 212 cnfe.printStackTrace(System.out); 213 } 214 } 215 } 216 217 /** 218 * Test a doubled class that extends the base class. 219 */ testExtend(ClassLoader loader)220 static void testExtend(ClassLoader loader) { 221 Class<?> doubledExtendClass; 222 Object obj; 223 224 /* get the "alternate" version of DoubledExtend */ 225 try { 226 doubledExtendClass = loader.loadClass("DoubledExtend"); 227 //System.out.println("+++ DoubledExtend is " + doubledExtendClass 228 // + " in " + doubledExtendClass.getClassLoader()); 229 } catch (ClassNotFoundException cnfe) { 230 System.out.println("loadClass failed: " + cnfe); 231 return; 232 } 233 234 /* instantiate */ 235 try { 236 obj = doubledExtendClass.newInstance(); 237 } catch (InstantiationException ie) { 238 System.out.println("newInstance failed: " + ie); 239 return; 240 } catch (IllegalAccessException iae) { 241 System.out.println("newInstance failed: " + iae); 242 return; 243 } catch (LinkageError le) { 244 System.out.println("Got expected LinkageError on DE"); 245 return; 246 } 247 248 /* use the base class reference to get a CL-specific instance */ 249 Base baseRef = (Base) obj; 250 DoubledExtend de = baseRef.getExtended(); 251 252 /* try to call through it */ 253 try { 254 String result; 255 256 result = Base.doStuff(de); 257 System.out.println("ERROR: did not get LinkageError on DE"); 258 System.out.println("(result=" + result + ")"); 259 } catch (LinkageError le) { 260 System.out.println("Got expected LinkageError on DE"); 261 return; 262 } 263 } 264 265 /** 266 * Test a doubled class that extends the base class, but is okay since 267 * it doesn't override the base class method. 268 */ testExtendOkay(ClassLoader loader)269 static void testExtendOkay(ClassLoader loader) { 270 Class<?> doubledExtendOkayClass; 271 Object obj; 272 273 /* get the "alternate" version of DoubledExtendOkay */ 274 try { 275 doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay"); 276 } catch (ClassNotFoundException cnfe) { 277 System.out.println("loadClass failed: " + cnfe); 278 return; 279 } 280 281 /* instantiate */ 282 try { 283 obj = doubledExtendOkayClass.newInstance(); 284 } catch (InstantiationException ie) { 285 System.out.println("newInstance failed: " + ie); 286 return; 287 } catch (IllegalAccessException iae) { 288 System.out.println("newInstance failed: " + iae); 289 return; 290 } catch (LinkageError le) { 291 System.out.println("Got unexpected LinkageError on DEO"); 292 le.printStackTrace(System.out); 293 return; 294 } 295 296 /* use the base class reference to get a CL-specific instance */ 297 BaseOkay baseRef = (BaseOkay) obj; 298 DoubledExtendOkay de = baseRef.getExtended(); 299 300 /* try to call through it */ 301 try { 302 String result; 303 304 result = BaseOkay.doStuff(de); 305 System.out.println("Got DEO result " + result); 306 } catch (LinkageError le) { 307 System.out.println("Got unexpected LinkageError on DEO"); 308 le.printStackTrace(System.out); 309 return; 310 } 311 } 312 313 /** 314 * Try to access a doubled class through a class that implements 315 * an interface declared in a different class. 316 */ testInterface(ClassLoader loader)317 static void testInterface(ClassLoader loader) { 318 Class<?> getDoubledClass; 319 Object obj; 320 321 /* get GetDoubled from the "alternate" class loader */ 322 try { 323 getDoubledClass = loader.loadClass("GetDoubled"); 324 } catch (ClassNotFoundException cnfe) { 325 System.out.println("loadClass failed: " + cnfe); 326 return; 327 } 328 329 /* instantiate */ 330 try { 331 obj = getDoubledClass.newInstance(); 332 } catch (InstantiationException ie) { 333 System.out.println("newInstance failed: " + ie); 334 return; 335 } catch (IllegalAccessException iae) { 336 System.out.println("newInstance failed: " + iae); 337 return; 338 } catch (LinkageError le) { 339 // Dalvik bails here 340 System.out.println("Got LinkageError on GD"); 341 return; 342 } 343 344 /* 345 * Cast the object to the interface, and try to use it. 346 */ 347 IGetDoubled iface = (IGetDoubled) obj; 348 try { 349 /* "de" will be the wrong variety of DoubledExtendOkay */ 350 DoubledExtendOkay de = iface.getDoubled(); 351 // reference impl bails here 352 String str = de.getStr(); 353 } catch (LinkageError le) { 354 System.out.println("Got LinkageError on GD"); 355 return; 356 } 357 System.out.println("Should have failed by now on GetDoubled"); 358 } 359 360 /** 361 * Throw an abstract class into the middle and see what happens. 362 */ testAbstract(ClassLoader loader)363 static void testAbstract(ClassLoader loader) { 364 Class<?> abstractGetClass; 365 Object obj; 366 367 /* get AbstractGet from the "alternate" loader */ 368 try { 369 abstractGetClass = loader.loadClass("AbstractGet"); 370 } catch (ClassNotFoundException cnfe) { 371 System.out.println("loadClass ta failed: " + cnfe); 372 return; 373 } 374 375 /* instantiate */ 376 try { 377 obj = abstractGetClass.newInstance(); 378 } catch (InstantiationException ie) { 379 System.out.println("newInstance failed: " + ie); 380 return; 381 } catch (IllegalAccessException iae) { 382 System.out.println("newInstance failed: " + iae); 383 return; 384 } catch (LinkageError le) { 385 System.out.println("Got LinkageError on TA"); 386 return; 387 } 388 389 /* use the base class reference to get a CL-specific instance */ 390 BaseOkay baseRef = (BaseOkay) obj; 391 DoubledExtendOkay de = baseRef.getExtended(); 392 393 /* try to call through it */ 394 try { 395 String result; 396 397 result = BaseOkay.doStuff(de); 398 } catch (LinkageError le) { 399 System.out.println("Got LinkageError on TA"); 400 return; 401 } 402 System.out.println("Should have failed by now in testAbstract"); 403 } 404 405 /** 406 * Test a doubled class that implements a common interface. 407 */ testImplement(ClassLoader loader)408 static void testImplement(ClassLoader loader) { 409 Class<?> doubledImplementClass; 410 Object obj; 411 412 useImplement(new DoubledImplement(), true); 413 414 /* get the "alternate" version of DoubledImplement */ 415 try { 416 doubledImplementClass = loader.loadClass("DoubledImplement"); 417 } catch (ClassNotFoundException cnfe) { 418 System.out.println("loadClass failed: " + cnfe); 419 return; 420 } 421 422 /* instantiate */ 423 try { 424 obj = doubledImplementClass.newInstance(); 425 } catch (InstantiationException ie) { 426 System.out.println("newInstance failed: " + ie); 427 return; 428 } catch (IllegalAccessException iae) { 429 System.out.println("newInstance failed: " + iae); 430 return; 431 } catch (LinkageError le) { 432 System.out.println("Got LinkageError on DI (early)"); 433 return; 434 } 435 436 /* if we lived this long, try to do something with it */ 437 ICommon icommon = (ICommon) obj; 438 useImplement(icommon.getDoubledInstance(), false); 439 } 440 441 /** 442 * Do something with a DoubledImplement instance. 443 */ useImplement(DoubledImplement di, boolean isOne)444 static void useImplement(DoubledImplement di, boolean isOne) { 445 //System.out.println("useObject: " + di.toString() + " -- " 446 // + di.getClass().getClassLoader()); 447 try { 448 di.one(); 449 if (!isOne) { 450 System.out.println("ERROR: did not get LinkageError on DI"); 451 } 452 } catch (LinkageError le) { 453 if (!isOne) { 454 System.out.println("Got LinkageError on DI (late)"); 455 } else { 456 throw le; 457 } 458 } 459 } 460 461 462 /** 463 * Test a class that implements an interface with a super-interface 464 * that refers to a doubled class. 465 */ testIfaceImplement(ClassLoader loader)466 static void testIfaceImplement(ClassLoader loader) { 467 Class<?> ifaceImplClass; 468 Object obj; 469 470 /* 471 * Create an instance of IfaceImpl. We also pull in 472 * DoubledImplement2 from the other class loader; without this 473 * we don't fail in some implementations. 474 */ 475 try { 476 ifaceImplClass = loader.loadClass("IfaceImpl"); 477 ifaceImplClass = loader.loadClass("DoubledImplement2"); 478 } catch (ClassNotFoundException cnfe) { 479 System.out.println("loadClass failed: " + cnfe); 480 return; 481 } 482 483 /* instantiate */ 484 try { 485 obj = ifaceImplClass.newInstance(); 486 } catch (InstantiationException ie) { 487 System.out.println("newInstance failed: " + ie); 488 return; 489 } catch (IllegalAccessException iae) { 490 System.out.println("newInstance failed: " + iae); 491 return; 492 } catch (LinkageError le) { 493 System.out.println("Got LinkageError on IDI (early)"); 494 //System.out.println(le); 495 return; 496 } 497 498 /* 499 * Without the pre-load of FancyLoader->DoubledImplement2, some 500 * implementations will happily execute through this part. "obj" 501 * comes from FancyLoader, but the di2 returned from ifaceSuper 502 * comes from the application class loader. 503 */ 504 IfaceSuper ifaceSuper = (IfaceSuper) obj; 505 DoubledImplement2 di2 = ifaceSuper.getDoubledInstance2(); 506 di2.one(); 507 } 508 testClassForName()509 static void testClassForName() throws Exception { 510 System.out.println(Class.forName("Main").toString()); 511 try { 512 System.out.println(Class.forName("Main", false, null).toString()); 513 } catch (ClassNotFoundException expected) { 514 System.out.println("Got expected ClassNotFoundException"); 515 } 516 } 517 } 518