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 /* 18 * Implementation of java.lang.reflect.Proxy. 19 * 20 * Traditionally this is implemented entirely in interpreted code, 21 * generating bytecode that defines the proxy class. Dalvik doesn't 22 * currently support this approach, so we generate the class directly. If 23 * we add support for DefineClass with standard classfiles we can 24 * eliminate this. 25 */ 26 #include "Dalvik.h" 27 28 #include <stdlib.h> 29 30 // fwd 31 static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod); 32 static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\ 33 ArrayObject** pThrows, int* pMethodCount); 34 static int copyWithoutDuplicates(Method** allMethods, int allCount, 35 Method** outMethods, ArrayObject* throws); 36 static bool createExceptionClassList(const Method* method, 37 PointerSet** pThrows); 38 static void updateExceptionClassList(const Method* method, PointerSet* throws); 39 static void createConstructor(ClassObject* clazz, Method* meth); 40 static void createHandlerMethod(ClassObject* clazz, Method* dstMeth, 41 const Method* srcMeth); 42 static void proxyConstructor(const u4* args, JValue* pResult, 43 const Method* method, Thread* self); 44 static void proxyInvoker(const u4* args, JValue* pResult, 45 const Method* method, Thread* self); 46 static bool mustWrapException(const Method* method, const Object* throwable); 47 48 /* private static fields in the Proxy class */ 49 #define kThrowsField 0 50 51 52 /* 53 * Perform Proxy setup. 54 */ 55 bool dvmReflectProxyStartup() 56 { 57 /* 58 * Standard methods we must provide in our proxy. 59 */ 60 Method* methE; 61 Method* methH; 62 Method* methT; 63 Method* methF; 64 methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, 65 "equals", "(Ljava/lang/Object;)Z"); 66 methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, 67 "hashCode", "()I"); 68 methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, 69 "toString", "()Ljava/lang/String;"); 70 methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, 71 "finalize", "()V"); 72 if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) { 73 LOGE("Could not find equals/hashCode/toString/finalize in Object\n"); 74 return false; 75 } 76 gDvm.voffJavaLangObject_equals = methE->methodIndex; 77 gDvm.voffJavaLangObject_hashCode = methH->methodIndex; 78 gDvm.voffJavaLangObject_toString = methT->methodIndex; 79 gDvm.voffJavaLangObject_finalize = methF->methodIndex; 80 81 /* 82 * The prototype signature needs to be cloned from a method in a 83 * "real" DEX file. We declared this otherwise unused method just 84 * for this purpose. 85 */ 86 ClassObject* proxyClass; 87 Method* meth; 88 proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;"); 89 if (proxyClass == NULL) { 90 LOGE("No java.lang.reflect.Proxy\n"); 91 return false; 92 } 93 meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype", 94 "(Ljava/lang/reflect/InvocationHandler;)V"); 95 if (meth == NULL) { 96 LOGE("Could not find java.lang.Proxy.constructorPrototype()\n"); 97 return false; 98 } 99 gDvm.methJavaLangReflectProxy_constructorPrototype = meth; 100 101 /* 102 * Get the offset of the "h" field in Proxy. 103 */ 104 gDvm.offJavaLangReflectProxy_h = dvmFindFieldOffset(proxyClass, "h", 105 "Ljava/lang/reflect/InvocationHandler;"); 106 if (gDvm.offJavaLangReflectProxy_h < 0) { 107 LOGE("Unable to find 'h' field in java.lang.Proxy\n"); 108 return false; 109 } 110 111 return true; 112 } 113 114 115 /* 116 * Generate a proxy class with the specified name, interfaces, and loader. 117 * "interfaces" is an array of class objects. 118 * 119 * The Proxy.getProxyClass() code has done the following: 120 * - Verified that "interfaces" contains only interfaces 121 * - Verified that no interface appears twice 122 * - Prepended the package name to the class name if one or more 123 * interfaces are non-public 124 * - Searched for an existing instance of an appropriate Proxy class 125 * 126 * On failure we leave a partially-created class object sitting around, 127 * but the garbage collector will take care of it. 128 */ 129 ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, 130 Object* loader) 131 { 132 int result = -1; 133 char* nameStr = NULL; 134 Method** methods = NULL; 135 ArrayObject* throws = NULL; 136 ClassObject* newClass = NULL; 137 int i; 138 139 nameStr = dvmCreateCstrFromString(str); 140 if (nameStr == NULL) { 141 dvmThrowException("Ljava/lang/IllegalArgumentException;", 142 "missing name"); 143 goto bail; 144 } 145 146 LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n", 147 nameStr, loader, interfaces->length); 148 149 150 /* 151 * Characteristics of a Proxy class: 152 * - concrete class, public and final 153 * - superclass is java.lang.reflect.Proxy 154 * - implements all listed interfaces (req'd for instanceof) 155 * - has one method for each method in the interfaces (for duplicates, 156 * the method in the earliest interface wins) 157 * - has one constructor (takes an InvocationHandler arg) 158 * - has overrides for hashCode, equals, and toString (these come first) 159 * - has one field, a reference to the InvocationHandler object, inherited 160 * from Proxy 161 * 162 * TODO: set protection domain so it matches bootstrap classes. 163 * 164 * The idea here is to create a class object and fill in the details 165 * as we would in loadClassFromDex(), and then call dvmLinkClass() to do 166 * all the heavy lifting (notably populating the virtual and interface 167 * method tables). 168 */ 169 170 /* 171 * Generate a temporary list of virtual methods. 172 */ 173 int methodCount = -1; 174 if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) 175 goto bail; 176 177 /* 178 * Allocate storage for the class object and set some basic fields. 179 */ 180 newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); 181 if (newClass == NULL) 182 goto bail; 183 DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); 184 dvmSetClassSerialNumber(newClass); 185 newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); 186 newClass->descriptor = newClass->descriptorAlloc; 187 newClass->accessFlags = ACC_PUBLIC | ACC_FINAL; 188 newClass->super = gDvm.classJavaLangReflectProxy; 189 newClass->primitiveType = PRIM_NOT; 190 newClass->classLoader = loader; 191 #if WITH_HPROF && WITH_HPROF_STACK 192 hprofFillInStackTrace(newClass); 193 #endif 194 195 /* 196 * Add direct method definitions. We have one (the constructor). 197 */ 198 newClass->directMethodCount = 1; 199 newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader, 200 1 * sizeof(Method)); 201 createConstructor(newClass, &newClass->directMethods[0]); 202 dvmLinearReadOnly(newClass->classLoader, newClass->directMethods); 203 204 /* 205 * Add virtual method definitions. 206 */ 207 newClass->virtualMethodCount = methodCount; 208 newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader, 209 newClass->virtualMethodCount * sizeof(Method)); 210 for (i = 0; i < newClass->virtualMethodCount; i++) { 211 createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]); 212 } 213 dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods); 214 215 /* 216 * Add interface list. 217 */ 218 int interfaceCount = interfaces->length; 219 ClassObject** ifArray = (ClassObject**) interfaces->contents; 220 newClass->interfaceCount = interfaceCount; 221 newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, 222 sizeof(ClassObject*) * interfaceCount); 223 for (i = 0; i < interfaceCount; i++) 224 newClass->interfaces[i] = ifArray[i]; 225 dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); 226 227 /* 228 * Static field list. We have one private field, for our list of 229 * exceptions declared for each method. 230 */ 231 newClass->sfieldCount = 1; 232 newClass->sfields = (StaticField*) calloc(1, sizeof(StaticField)); 233 StaticField* sfield = &newClass->sfields[kThrowsField]; 234 sfield->field.clazz = newClass; 235 sfield->field.name = "throws"; 236 sfield->field.signature = "[[Ljava/lang/Throwable;"; 237 sfield->field.accessFlags = ACC_STATIC | ACC_PRIVATE; 238 dvmSetStaticFieldObject(sfield, (Object*)throws); 239 240 /* 241 * Everything is ready. See if the linker will lap it up. 242 */ 243 newClass->status = CLASS_LOADED; 244 if (!dvmLinkClass(newClass, true)) { 245 LOGD("Proxy class link failed\n"); 246 goto bail; 247 } 248 249 /* 250 * All good. Add it to the hash table. We should NOT see a collision 251 * here; if we do, it means the caller has screwed up and provided us 252 * with a duplicate name. 253 */ 254 if (!dvmAddClassToHash(newClass)) { 255 LOGE("ERROR: attempted to generate %s more than once\n", 256 newClass->descriptor); 257 goto bail; 258 } 259 260 result = 0; 261 262 bail: 263 free(nameStr); 264 free(methods); 265 if (result != 0) { 266 /* must free innards explicitly if we didn't finish linking */ 267 dvmFreeClassInnards(newClass); 268 newClass = NULL; 269 if (!dvmCheckException(dvmThreadSelf())) { 270 /* throw something */ 271 dvmThrowException("Ljava/lang/RuntimeException;", NULL); 272 } 273 } 274 275 /* allow the GC to free these when nothing else has a reference */ 276 dvmReleaseTrackedAlloc((Object*) throws, NULL); 277 dvmReleaseTrackedAlloc((Object*) newClass, NULL); 278 279 return newClass; 280 } 281 282 283 /* 284 * Generate a list of methods. The Method pointers returned point to the 285 * abstract method definition from the appropriate interface, or to the 286 * virtual method definition in java.lang.Object. 287 * 288 * We also allocate an array of arrays of throwable classes, one for each 289 * method,so we can do some special handling of checked exceptions. The 290 * caller must call ReleaseTrackedAlloc() on *pThrows. 291 */ 292 static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, 293 ArrayObject** pThrows, int* pMethodCount) 294 { 295 ClassObject** classes; 296 ArrayObject* throws = NULL; 297 Method** methods = NULL; 298 Method** allMethods = NULL; 299 int numInterfaces, maxCount, actualCount, allCount; 300 bool result = false; 301 int i; 302 303 /* 304 * Get a maximum count so we can allocate storage. We need the 305 * methods declared by each interface and all of its superinterfaces. 306 */ 307 maxCount = 3; // 3 methods in java.lang.Object 308 numInterfaces = interfaces->length; 309 classes = (ClassObject**) interfaces->contents; 310 311 for (i = 0; i < numInterfaces; i++, classes++) { 312 ClassObject* clazz = *classes; 313 314 LOGVV("--- %s virtualMethodCount=%d\n", 315 clazz->descriptor, clazz->virtualMethodCount); 316 maxCount += clazz->virtualMethodCount; 317 318 int j; 319 for (j = 0; j < clazz->iftableCount; j++) { 320 ClassObject* iclass = clazz->iftable[j].clazz; 321 322 LOGVV("--- +%s %d\n", 323 iclass->descriptor, iclass->virtualMethodCount); 324 maxCount += iclass->virtualMethodCount; 325 } 326 } 327 328 methods = (Method**) malloc(maxCount * sizeof(*methods)); 329 allMethods = (Method**) malloc(maxCount * sizeof(*methods)); 330 if (methods == NULL || allMethods == NULL) 331 goto bail; 332 333 /* 334 * First three entries are the java.lang.Object methods. 335 */ 336 ClassObject* obj = gDvm.classJavaLangObject; 337 allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals]; 338 allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode]; 339 allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString]; 340 allCount = 3; 341 342 /* 343 * Add the methods from each interface, in order. 344 */ 345 classes = (ClassObject**) interfaces->contents; 346 for (i = 0; i < numInterfaces; i++, classes++) { 347 ClassObject* clazz = *classes; 348 int j; 349 350 for (j = 0; j < clazz->virtualMethodCount; j++) { 351 allMethods[allCount++] = &clazz->virtualMethods[j]; 352 } 353 354 for (j = 0; j < clazz->iftableCount; j++) { 355 ClassObject* iclass = clazz->iftable[j].clazz; 356 int k; 357 358 for (k = 0; k < iclass->virtualMethodCount; k++) { 359 allMethods[allCount++] = &iclass->virtualMethods[k]; 360 } 361 } 362 } 363 assert(allCount == maxCount); 364 365 /* 366 * Allocate some storage to hold the lists of throwables. We need 367 * one entry per unique method, but it's convenient to allocate it 368 * ahead of the duplicate processing. 369 */ 370 ClassObject* arrArrClass; 371 arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL); 372 if (arrArrClass == NULL) 373 goto bail; 374 throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT); 375 376 /* 377 * Identify and remove duplicates. 378 */ 379 actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws); 380 if (actualCount < 0) 381 goto bail; 382 383 //LOGI("gathered methods:\n"); 384 //for (i = 0; i < actualCount; i++) { 385 // LOGI(" %d: %s.%s\n", 386 // i, methods[i]->clazz->descriptor, methods[i]->name); 387 //} 388 389 *pMethods = methods; 390 *pMethodCount = actualCount; 391 *pThrows = throws; 392 result = true; 393 394 bail: 395 free(allMethods); 396 if (!result) { 397 free(methods); 398 dvmReleaseTrackedAlloc((Object*)throws, NULL); 399 } 400 return result; 401 } 402 403 /* 404 * Identify and remove duplicates, where "duplicate" means it has the 405 * same name and arguments, but not necessarily the same return type. 406 * 407 * If duplicate methods have different return types, we want to use the 408 * first method whose return type is assignable from all other duplicate 409 * methods. That is, if we have: 410 * class base {...} 411 * class sub extends base {...} 412 * class subsub extends sub {...} 413 * Then we want to return the method that returns subsub, since callers 414 * to any form of the method will get a usable object back. 415 * 416 * All other duplicate methods are stripped out. 417 * 418 * This also populates the "throwLists" array with arrays of Class objects, 419 * one entry per method in "outMethods". Methods that don't declare any 420 * throwables (or have no common throwables with duplicate methods) will 421 * have NULL entries. 422 * 423 * Returns the number of methods copied into "methods", or -1 on failure. 424 */ 425 static int copyWithoutDuplicates(Method** allMethods, int allCount, 426 Method** outMethods, ArrayObject* throwLists) 427 { 428 Method* best; 429 int outCount = 0; 430 int i, j; 431 432 /* 433 * The plan is to run through all methods, checking all other methods 434 * for a duplicate. If we find a match, we see if the other methods' 435 * return type is compatible/assignable with ours. If the current 436 * method is assignable from all others, we copy it to the new list, 437 * and NULL out all other entries. If not, we keep looking for a 438 * better version. 439 * 440 * If there are no duplicates, we copy the method and NULL the entry. 441 * 442 * At the end of processing, if we have any non-NULL entries, then we 443 * have bad duplicates and must exit with an exception. 444 */ 445 for (i = 0; i < allCount; i++) { 446 bool best, dupe; 447 448 if (allMethods[i] == NULL) 449 continue; 450 451 /* 452 * Find all duplicates. If any of the return types is not 453 * assignable to our return type, then we're not the best. 454 * 455 * We start from 0, not i, because we need to compare assignability 456 * the other direction even if we've compared these before. 457 */ 458 dupe = false; 459 best = true; 460 for (j = 0; j < allCount; j++) { 461 if (i == j) 462 continue; 463 if (allMethods[j] == NULL) 464 continue; 465 466 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i], 467 allMethods[j]) == 0) 468 { 469 /* 470 * Duplicate method, check return type. If it's a primitive 471 * type or void, the types must match exactly, or we throw 472 * an exception now. 473 */ 474 LOGV("MATCH on %s.%s and %s.%s\n", 475 allMethods[i]->clazz->descriptor, allMethods[i]->name, 476 allMethods[j]->clazz->descriptor, allMethods[j]->name); 477 dupe = true; 478 if (!returnTypesAreCompatible(allMethods[i], allMethods[j])) 479 best = false; 480 } 481 } 482 483 /* 484 * If this is the best of a set of duplicates, copy it over and 485 * nuke all duplicates. 486 * 487 * While we do this, we create the set of exceptions declared to 488 * be thrown by all occurrences of the method. 489 */ 490 if (dupe) { 491 if (best) { 492 LOGV("BEST %d %s.%s -> %d\n", i, 493 allMethods[i]->clazz->descriptor, allMethods[i]->name, 494 outCount); 495 496 /* if we have exceptions, make a local copy */ 497 PointerSet* commonThrows = NULL; 498 if (!createExceptionClassList(allMethods[i], &commonThrows)) 499 return -1; 500 501 /* 502 * Run through one more time, erasing the duplicates. (This 503 * would go faster if we had marked them somehow.) 504 */ 505 for (j = 0; j < allCount; j++) { 506 if (i == j) 507 continue; 508 if (allMethods[j] == NULL) 509 continue; 510 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i], 511 allMethods[j]) == 0) 512 { 513 LOGV("DEL %d %s.%s\n", j, 514 allMethods[j]->clazz->descriptor, 515 allMethods[j]->name); 516 517 /* 518 * Update set to hold the intersection of method[i]'s 519 * and method[j]'s throws. 520 */ 521 if (commonThrows != NULL) { 522 updateExceptionClassList(allMethods[j], 523 commonThrows); 524 } 525 526 allMethods[j] = NULL; 527 } 528 } 529 530 /* 531 * If the set of Throwable classes isn't empty, create an 532 * array of Class, copy them into it, and put the result 533 * into the "throwLists" array. 534 */ 535 if (commonThrows != NULL && 536 dvmPointerSetGetCount(commonThrows) > 0) 537 { 538 int commonCount = dvmPointerSetGetCount(commonThrows); 539 ArrayObject* throwArray; 540 Object** contents; 541 int ent; 542 543 throwArray = dvmAllocArrayByClass( 544 gDvm.classJavaLangClassArray, commonCount, 545 ALLOC_DEFAULT); 546 if (throwArray == NULL) { 547 LOGE("common-throw array alloc failed\n"); 548 return -1; 549 } 550 551 contents = (Object**) throwArray->contents; 552 for (ent = 0; ent < commonCount; ent++) { 553 contents[ent] = (Object*) 554 dvmPointerSetGetEntry(commonThrows, ent); 555 } 556 557 /* add it to the array of arrays */ 558 contents = (Object**) throwLists->contents; 559 contents[outCount] = (Object*) throwArray; 560 dvmReleaseTrackedAlloc((Object*) throwArray, NULL); 561 } 562 563 /* copy the winner and NULL it out */ 564 outMethods[outCount++] = allMethods[i]; 565 allMethods[i] = NULL; 566 567 dvmPointerSetFree(commonThrows); 568 } else { 569 LOGV("BEST not %d\n", i); 570 } 571 } else { 572 /* 573 * Singleton. Copy the entry and NULL it out. 574 */ 575 LOGV("COPY singleton %d %s.%s -> %d\n", i, 576 allMethods[i]->clazz->descriptor, allMethods[i]->name, 577 outCount); 578 579 /* keep track of our throwables */ 580 ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]); 581 if (exceptionArray != NULL) { 582 Object** contents; 583 584 contents = (Object**) throwLists->contents; 585 contents[outCount] = (Object*) exceptionArray; 586 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); 587 } 588 589 outMethods[outCount++] = allMethods[i]; 590 allMethods[i] = NULL; 591 } 592 } 593 594 /* 595 * Check for stragglers. If we find any, throw an exception. 596 */ 597 for (i = 0; i < allCount; i++) { 598 if (allMethods[i] != NULL) { 599 LOGV("BAD DUPE: %d %s.%s\n", i, 600 allMethods[i]->clazz->descriptor, allMethods[i]->name); 601 dvmThrowException("Ljava/lang/IllegalArgumentException;", 602 "incompatible return types in proxied interfaces"); 603 return -1; 604 } 605 } 606 607 return outCount; 608 } 609 610 611 /* 612 * Classes can declare to throw multiple exceptions in a hierarchy, e.g. 613 * IOException and FileNotFoundException. Since we're only interested in 614 * knowing the set that can be thrown without requiring an extra wrapper, 615 * we can remove anything that is a subclass of something else in the list. 616 * 617 * The "mix" step we do next reduces things toward the most-derived class, 618 * so it's important that we start with the least-derived classes. 619 */ 620 static void reduceExceptionClassList(ArrayObject* exceptionArray) 621 { 622 const ClassObject** classes = (const ClassObject**)exceptionArray->contents; 623 int len = exceptionArray->length; 624 int i, j; 625 626 /* 627 * Consider all pairs of classes. If one is the subclass of the other, 628 * null out the subclass. 629 */ 630 for (i = 0; i < len-1; i++) { 631 if (classes[i] == NULL) 632 continue; 633 for (j = i + 1; j < len; j++) { 634 if (classes[j] == NULL) 635 continue; 636 637 if (dvmInstanceof(classes[i], classes[j])) { 638 classes[i] = NULL; 639 break; /* no more comparisons against classes[i] */ 640 } else if (dvmInstanceof(classes[j], classes[i])) { 641 classes[j] = NULL; 642 } 643 } 644 } 645 } 646 647 /* 648 * Create a local array with a copy of the throwable classes declared by 649 * "method". If no throws are declared, "*pSet" will be NULL. 650 * 651 * Returns "false" on allocation failure. 652 */ 653 static bool createExceptionClassList(const Method* method, PointerSet** pThrows) 654 { 655 ArrayObject* exceptionArray = NULL; 656 bool result = false; 657 658 exceptionArray = dvmGetMethodThrows(method); 659 if (exceptionArray != NULL && exceptionArray->length > 0) { 660 /* reduce list, nulling out redundant entries */ 661 reduceExceptionClassList(exceptionArray); 662 663 *pThrows = dvmPointerSetAlloc(exceptionArray->length); 664 if (*pThrows == NULL) 665 goto bail; 666 667 const ClassObject** contents; 668 int i; 669 670 contents = (const ClassObject**) exceptionArray->contents; 671 for (i = 0; i < (int) exceptionArray->length; i++) { 672 if (contents[i] != NULL) 673 dvmPointerSetAddEntry(*pThrows, contents[i]); 674 } 675 } else { 676 *pThrows = NULL; 677 } 678 679 result = true; 680 681 bail: 682 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); 683 return result; 684 } 685 686 /* 687 * We need to compute the intersection of the arguments, i.e. remove 688 * anything from "throws" that isn't in the method's list of throws. 689 * 690 * If one class is a subclass of another, we want to keep just the subclass, 691 * moving toward the most-restrictive set. 692 * 693 * We assume these are all classes, and don't try to filter out interfaces. 694 */ 695 static void updateExceptionClassList(const Method* method, PointerSet* throws) 696 { 697 int setSize = dvmPointerSetGetCount(throws); 698 if (setSize == 0) 699 return; 700 701 ArrayObject* exceptionArray = dvmGetMethodThrows(method); 702 if (exceptionArray == NULL) { 703 /* nothing declared, so intersection is empty */ 704 dvmPointerSetClear(throws); 705 return; 706 } 707 708 /* reduce list, nulling out redundant entries */ 709 reduceExceptionClassList(exceptionArray); 710 711 int mixLen = dvmPointerSetGetCount(throws); 712 const ClassObject* mixSet[mixLen]; 713 714 int declLen = exceptionArray->length; 715 const ClassObject** declSet = (const ClassObject**)exceptionArray->contents; 716 717 int i, j; 718 719 /* grab a local copy to work on */ 720 for (i = 0; i < mixLen; i++) { 721 mixSet[i] = dvmPointerSetGetEntry(throws, i); 722 } 723 724 for (i = 0; i < mixLen; i++) { 725 for (j = 0; j < declLen; j++) { 726 if (declSet[j] == NULL) 727 continue; 728 729 if (mixSet[i] == declSet[j]) { 730 /* match, keep this one */ 731 break; 732 } else if (dvmInstanceof(mixSet[i], declSet[j])) { 733 /* mix is a subclass of a declared throwable, keep it */ 734 break; 735 } else if (dvmInstanceof(declSet[j], mixSet[i])) { 736 /* mix is a superclass, replace it */ 737 mixSet[i] = declSet[j]; 738 break; 739 } 740 } 741 742 if (j == declLen) { 743 /* no match, remove entry by nulling it out */ 744 mixSet[i] = NULL; 745 } 746 } 747 748 /* copy results back out; this eliminates duplicates as we go */ 749 dvmPointerSetClear(throws); 750 for (i = 0; i < mixLen; i++) { 751 if (mixSet[i] != NULL) 752 dvmPointerSetAddEntry(throws, mixSet[i]); 753 } 754 755 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); 756 } 757 758 759 /* 760 * Check to see if the return types are compatible. 761 * 762 * If the return type is primitive or void, it must match exactly. 763 * 764 * If not, the type in "subMethod" must be assignable to the type in 765 * "baseMethod". 766 */ 767 static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod) 768 { 769 const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype); 770 const char* subSig = dexProtoGetReturnType(&subMethod->prototype); 771 ClassObject* baseClass; 772 ClassObject* subClass; 773 774 if (baseSig[1] == '\0' || subSig[1] == '\0') { 775 /* at least one is primitive type */ 776 return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]); 777 } 778 779 baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader); 780 subClass = dvmFindClass(subSig, subMethod->clazz->classLoader); 781 bool result = dvmInstanceof(subClass, baseClass); 782 return result; 783 } 784 785 /* 786 * Create a constructor for our Proxy class. The constructor takes one 787 * argument, a java.lang.reflect.InvocationHandler. 788 */ 789 static void createConstructor(ClassObject* clazz, Method* meth) 790 { 791 meth->clazz = clazz; 792 meth->accessFlags = ACC_PUBLIC | ACC_NATIVE; 793 meth->name = "<init>"; 794 meth->prototype = 795 gDvm.methJavaLangReflectProxy_constructorPrototype->prototype; 796 meth->shorty = 797 gDvm.methJavaLangReflectProxy_constructorPrototype->shorty; 798 // no pDexCode or pDexMethod 799 800 int argsSize = dvmComputeMethodArgsSize(meth) + 1; 801 meth->registersSize = meth->insSize = argsSize; 802 803 meth->nativeFunc = proxyConstructor; 804 } 805 806 /* 807 * Create a method in our Proxy class with the name and signature of 808 * the interface method it implements. 809 */ 810 static void createHandlerMethod(ClassObject* clazz, Method* dstMeth, 811 const Method* srcMeth) 812 { 813 dstMeth->clazz = clazz; 814 dstMeth->insns = (u2*) srcMeth; 815 dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE; 816 dstMeth->name = srcMeth->name; 817 dstMeth->prototype = srcMeth->prototype; 818 dstMeth->shorty = srcMeth->shorty; 819 // no pDexCode or pDexMethod 820 821 int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1; 822 dstMeth->registersSize = dstMeth->insSize = argsSize; 823 824 dstMeth->nativeFunc = proxyInvoker; 825 } 826 827 /* 828 * Return a new Object[] array with the contents of "args". We determine 829 * the number and types of values in "args" based on the method signature. 830 * Primitive types are boxed. 831 * 832 * Returns NULL if the method takes no arguments. 833 * 834 * The caller must call dvmReleaseTrackedAlloc() on the return value. 835 * 836 * On failure, returns with an appropriate exception raised. 837 */ 838 static ArrayObject* boxMethodArgs(const Method* method, const u4* args) 839 { 840 const char* desc = &method->shorty[1]; // [0] is the return type. 841 ArrayObject* argArray = NULL; 842 int argCount; 843 Object** argObjects; 844 bool failed = true; 845 846 /* count args */ 847 argCount = dexProtoGetParameterCount(&method->prototype); 848 849 /* allocate storage */ 850 argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount, 851 kObjectArrayRefWidth, ALLOC_DEFAULT); 852 if (argArray == NULL) 853 goto bail; 854 argObjects = (Object**) argArray->contents; 855 856 /* 857 * Fill in the array. 858 */ 859 860 int srcIndex = 0; 861 862 argCount = 0; 863 while (*desc != '\0') { 864 char descChar = *(desc++); 865 JValue value; 866 867 switch (descChar) { 868 case 'Z': 869 case 'C': 870 case 'F': 871 case 'B': 872 case 'S': 873 case 'I': 874 value.i = args[srcIndex++]; 875 argObjects[argCount] = (Object*) dvmWrapPrimitive(value, 876 dvmFindPrimitiveClass(descChar)); 877 /* argObjects is tracked, don't need to hold this too */ 878 dvmReleaseTrackedAlloc(argObjects[argCount], NULL); 879 argCount++; 880 break; 881 case 'D': 882 case 'J': 883 value.j = dvmGetArgLong(args, srcIndex); 884 srcIndex += 2; 885 argObjects[argCount] = (Object*) dvmWrapPrimitive(value, 886 dvmFindPrimitiveClass(descChar)); 887 dvmReleaseTrackedAlloc(argObjects[argCount], NULL); 888 argCount++; 889 break; 890 case '[': 891 case 'L': 892 argObjects[argCount++] = (Object*) args[srcIndex++]; 893 break; 894 } 895 } 896 897 failed = false; 898 899 bail: 900 if (failed) { 901 dvmReleaseTrackedAlloc((Object*)argArray, NULL); 902 argArray = NULL; 903 } 904 return argArray; 905 } 906 907 /* 908 * This is the constructor for a generated proxy object. All we need to 909 * do is stuff "handler" into "h". 910 */ 911 static void proxyConstructor(const u4* args, JValue* pResult, 912 const Method* method, Thread* self) 913 { 914 Object* obj = (Object*) args[0]; 915 Object* handler = (Object*) args[1]; 916 917 dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler); 918 } 919 920 /* 921 * This is the common message body for proxy methods. 922 * 923 * The method we're calling looks like: 924 * public Object invoke(Object proxy, Method method, Object[] args) 925 * 926 * This means we have to create a Method object, box our arguments into 927 * a new Object[] array, make the call, and unbox the return value if 928 * necessary. 929 */ 930 static void proxyInvoker(const u4* args, JValue* pResult, 931 const Method* method, Thread* self) 932 { 933 Object* thisObj = (Object*) args[0]; 934 Object* methodObj = NULL; 935 ArrayObject* argArray = NULL; 936 Object* handler; 937 Method* invoke; 938 ClassObject* returnType; 939 int hOffset; 940 JValue invokeResult; 941 942 /* 943 * Retrieve handler object for this proxy instance. The field is 944 * defined in the superclass (Proxy). 945 */ 946 handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h); 947 948 /* 949 * Find the invoke() method, looking in "this"s class. (Because we 950 * start here we don't have to convert it to a vtable index and then 951 * index into this' vtable.) 952 */ 953 invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", 954 "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); 955 if (invoke == NULL) { 956 LOGE("Unable to find invoke()\n"); 957 dvmAbort(); 958 } 959 960 LOGV("invoke: %s.%s, this=%p, handler=%s\n", 961 method->clazz->descriptor, method->name, 962 thisObj, handler->clazz->descriptor); 963 964 /* 965 * Create a java.lang.reflect.Method object for this method. 966 * 967 * We don't want to use "method", because that's the concrete 968 * implementation in the proxy class. We want the abstract Method 969 * from the declaring interface. We have a pointer to it tucked 970 * away in the "insns" field. 971 * 972 * TODO: this could be cached for performance. 973 */ 974 methodObj = dvmCreateReflectMethodObject((Method*) method->insns); 975 if (methodObj == NULL) { 976 assert(dvmCheckException(self)); 977 goto bail; 978 } 979 980 /* 981 * Determine the return type from the signature. 982 * 983 * TODO: this could be cached for performance. 984 */ 985 returnType = dvmGetBoxedReturnType(method); 986 if (returnType == NULL) { 987 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 988 LOGE("Could not determine return type for '%s'\n", desc); 989 free(desc); 990 assert(dvmCheckException(self)); 991 goto bail; 992 } 993 LOGV(" return type will be %s\n", returnType->descriptor); 994 995 /* 996 * Convert "args" array into Object[] array, using the method 997 * signature to determine types. If the method takes no arguments, 998 * we must pass null. 999 */ 1000 argArray = boxMethodArgs(method, args+1); 1001 if (dvmCheckException(self)) 1002 goto bail; 1003 1004 /* 1005 * Call h.invoke(proxy, method, args). 1006 * 1007 * We don't need to repackage exceptions, so if one has been thrown 1008 * just jump to the end. 1009 */ 1010 dvmCallMethod(self, invoke, handler, &invokeResult, 1011 thisObj, methodObj, argArray); 1012 if (dvmCheckException(self)) { 1013 Object* excep = dvmGetException(self); 1014 if (mustWrapException(method, excep)) { 1015 /* wrap with UndeclaredThrowableException */ 1016 dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;"); 1017 } 1018 goto bail; 1019 } 1020 1021 /* 1022 * Unbox the return value. If it's the wrong type, throw a 1023 * ClassCastException. If it's a null pointer and we need a 1024 * primitive type, throw a NullPointerException. 1025 */ 1026 if (returnType->primitiveType == PRIM_VOID) { 1027 LOGVV("+++ ignoring return to void\n"); 1028 } else if (invokeResult.l == NULL) { 1029 if (dvmIsPrimitiveClass(returnType)) { 1030 dvmThrowException("Ljava/lang/NullPointerException;", 1031 "null result when primitive expected"); 1032 goto bail; 1033 } 1034 pResult->l = NULL; 1035 } else { 1036 if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { 1037 dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", 1038 ((Object*)invokeResult.l)->clazz->descriptor); 1039 goto bail; 1040 } 1041 } 1042 1043 bail: 1044 dvmReleaseTrackedAlloc(methodObj, self); 1045 dvmReleaseTrackedAlloc((Object*)argArray, self); 1046 } 1047 1048 /* 1049 * Determine if it's okay for this method to throw this exception. If 1050 * an unchecked exception was thrown we immediately return false. If 1051 * checked, we have to ensure that this method and all of its duplicates 1052 * have declared that they throw it. 1053 */ 1054 static bool mustWrapException(const Method* method, const Object* throwable) 1055 { 1056 const ArrayObject* throws; 1057 const ArrayObject* methodThrows; 1058 const Object** contents; 1059 const ClassObject** classes; 1060 1061 if (!dvmIsCheckedException(throwable)) 1062 return false; 1063 1064 const StaticField* sfield = &method->clazz->sfields[kThrowsField]; 1065 throws = (ArrayObject*) dvmGetStaticFieldObject(sfield); 1066 1067 int methodIndex = method - method->clazz->virtualMethods; 1068 assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount); 1069 1070 contents = (const Object**) throws->contents; 1071 methodThrows = (ArrayObject*) contents[methodIndex]; 1072 1073 if (methodThrows == NULL) { 1074 /* no throws declared, must wrap all checked exceptions */ 1075 //printf("+++ methodThrows[%d] is null, wrapping all\n", methodIndex); 1076 return true; 1077 } 1078 1079 int throwCount = methodThrows->length; 1080 classes = (const ClassObject**) methodThrows->contents; 1081 int i; 1082 1083 //printf("%s.%s list:\n", method->clazz->descriptor, method->name); 1084 //for (i = 0; i < throwCount; i++) 1085 // printf(" %d: %s\n", i, classes[i]->descriptor); 1086 1087 for (i = 0; i < throwCount; i++) { 1088 if (dvmInstanceof(throwable->clazz, classes[i])) { 1089 /* this was declared, okay to throw */ 1090 return false; 1091 } 1092 } 1093 1094 /* no match in declared throws */ 1095 return true; 1096 } 1097 1098