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