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