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