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 * Operations on an Object. 19 */ 20 #include "Dalvik.h" 21 22 /* 23 * Find a matching field, in the current class only. 24 * 25 * Returns NULL if the field can't be found. (Does not throw an exception.) 26 */ 27 InstField* dvmFindInstanceField(const ClassObject* clazz, 28 const char* fieldName, const char* signature) 29 { 30 InstField* pField; 31 int i; 32 33 assert(clazz != NULL); 34 35 /* 36 * Find a field with a matching name and signature. The Java programming 37 * language does not allow you to have two fields with the same name 38 * and different types, but the Java VM spec does allow it, so we can't 39 * bail out early when the name matches. 40 */ 41 pField = clazz->ifields; 42 for (i = 0; i < clazz->ifieldCount; i++, pField++) { 43 if (strcmp(fieldName, pField->name) == 0 && 44 strcmp(signature, pField->signature) == 0) 45 { 46 return pField; 47 } 48 } 49 50 return NULL; 51 } 52 53 /* 54 * Find a matching field, in this class or a superclass. 55 * 56 * Searching through interfaces isn't necessary, because interface fields 57 * are inherently public/static/final. 58 * 59 * Returns NULL if the field can't be found. (Does not throw an exception.) 60 */ 61 InstField* dvmFindInstanceFieldHier(const ClassObject* clazz, 62 const char* fieldName, const char* signature) 63 { 64 InstField* pField; 65 66 /* 67 * Search for a match in the current class. 68 */ 69 pField = dvmFindInstanceField(clazz, fieldName, signature); 70 if (pField != NULL) 71 return pField; 72 73 if (clazz->super != NULL) 74 return dvmFindInstanceFieldHier(clazz->super, fieldName, signature); 75 else 76 return NULL; 77 } 78 79 80 /* 81 * Find a matching field, in this class or an interface. 82 * 83 * Returns NULL if the field can't be found. (Does not throw an exception.) 84 */ 85 StaticField* dvmFindStaticField(const ClassObject* clazz, 86 const char* fieldName, const char* signature) 87 { 88 const StaticField* pField; 89 int i; 90 91 assert(clazz != NULL); 92 93 /* 94 * Find a field with a matching name and signature. As with instance 95 * fields, the VM allows you to have two fields with the same name so 96 * long as they have different types. 97 */ 98 pField = &clazz->sfields[0]; 99 for (i = 0; i < clazz->sfieldCount; i++, pField++) { 100 if (strcmp(fieldName, pField->name) == 0 && 101 strcmp(signature, pField->signature) == 0) 102 { 103 return (StaticField*) pField; 104 } 105 } 106 107 return NULL; 108 } 109 110 /* 111 * Find a matching field, in this class or a superclass. 112 * 113 * Returns NULL if the field can't be found. (Does not throw an exception.) 114 */ 115 StaticField* dvmFindStaticFieldHier(const ClassObject* clazz, 116 const char* fieldName, const char* signature) 117 { 118 StaticField* pField; 119 120 /* 121 * Search for a match in the current class. 122 */ 123 pField = dvmFindStaticField(clazz, fieldName, signature); 124 if (pField != NULL) 125 return pField; 126 127 /* 128 * See if it's in any of our interfaces. We don't check interfaces 129 * inherited from the superclass yet. 130 * 131 * (Note the set may have been stripped down because of redundancy with 132 * the superclass; see notes in createIftable.) 133 */ 134 int i = 0; 135 if (clazz->super != NULL) { 136 assert(clazz->iftableCount >= clazz->super->iftableCount); 137 i = clazz->super->iftableCount; 138 } 139 for ( ; i < clazz->iftableCount; i++) { 140 ClassObject* iface = clazz->iftable[i].clazz; 141 pField = dvmFindStaticField(iface, fieldName, signature); 142 if (pField != NULL) 143 return pField; 144 } 145 146 if (clazz->super != NULL) 147 return dvmFindStaticFieldHier(clazz->super, fieldName, signature); 148 else 149 return NULL; 150 } 151 152 /* 153 * Find a matching field, in this class or a superclass. 154 * 155 * We scan both the static and instance field lists in the class. If it's 156 * not found there, we check the direct interfaces, and then recursively 157 * scan the superclasses. This is the order prescribed in the VM spec 158 * (v2 5.4.3.2). 159 * 160 * In most cases we know that we're looking for either a static or an 161 * instance field and there's no value in searching through both types. 162 * During verification we need to recognize and reject certain unusual 163 * situations, and we won't see them unless we walk the lists this way. 164 */ 165 Field* dvmFindFieldHier(const ClassObject* clazz, const char* fieldName, 166 const char* signature) 167 { 168 Field* pField; 169 170 /* 171 * Search for a match in the current class. Which set we scan first 172 * doesn't really matter. 173 */ 174 pField = (Field*) dvmFindStaticField(clazz, fieldName, signature); 175 if (pField != NULL) 176 return pField; 177 pField = (Field*) dvmFindInstanceField(clazz, fieldName, signature); 178 if (pField != NULL) 179 return pField; 180 181 /* 182 * See if it's in any of our interfaces. We don't check interfaces 183 * inherited from the superclass yet. 184 */ 185 int i = 0; 186 if (clazz->super != NULL) { 187 assert(clazz->iftableCount >= clazz->super->iftableCount); 188 i = clazz->super->iftableCount; 189 } 190 for ( ; i < clazz->iftableCount; i++) { 191 ClassObject* iface = clazz->iftable[i].clazz; 192 pField = (Field*) dvmFindStaticField(iface, fieldName, signature); 193 if (pField != NULL) 194 return pField; 195 } 196 197 if (clazz->super != NULL) 198 return dvmFindFieldHier(clazz->super, fieldName, signature); 199 else 200 return NULL; 201 } 202 203 204 /* 205 * Compare the given name, return type, and argument types with the contents 206 * of the given method. This returns 0 if they are equal and non-zero if not. 207 */ 208 static inline int compareMethodHelper(Method* method, const char* methodName, 209 const char* returnType, size_t argCount, const char** argTypes) 210 { 211 DexParameterIterator iterator; 212 const DexProto* proto; 213 214 if (strcmp(methodName, method->name) != 0) { 215 return 1; 216 } 217 218 proto = &method->prototype; 219 220 if (strcmp(returnType, dexProtoGetReturnType(proto)) != 0) { 221 return 1; 222 } 223 224 if (dexProtoGetParameterCount(proto) != argCount) { 225 return 1; 226 } 227 228 dexParameterIteratorInit(&iterator, proto); 229 230 for (/*argCount*/; argCount != 0; argCount--, argTypes++) { 231 const char* argType = *argTypes; 232 const char* paramType = dexParameterIteratorNextDescriptor(&iterator); 233 234 if (paramType == NULL) { 235 /* Param list ended early; no match */ 236 break; 237 } else if (strcmp(argType, paramType) != 0) { 238 /* Types aren't the same; no match. */ 239 break; 240 } 241 } 242 243 if (argCount == 0) { 244 /* We ran through all the given arguments... */ 245 if (dexParameterIteratorNextDescriptor(&iterator) == NULL) { 246 /* ...and through all the method's arguments; success! */ 247 return 0; 248 } 249 } 250 251 return 1; 252 } 253 254 /* 255 * Get the count of arguments in the given method descriptor string, 256 * and also find a pointer to the return type. 257 */ 258 static inline size_t countArgsAndFindReturnType(const char* descriptor, 259 const char** pReturnType) 260 { 261 size_t count = 0; 262 bool bogus = false; 263 bool done = false; 264 265 assert(*descriptor == '('); 266 descriptor++; 267 268 while (!done) { 269 switch (*descriptor) { 270 case 'B': case 'C': case 'D': case 'F': 271 case 'I': case 'J': case 'S': case 'Z': { 272 count++; 273 break; 274 } 275 case '[': { 276 do { 277 descriptor++; 278 } while (*descriptor == '['); 279 /* 280 * Don't increment count, as it will be taken care of 281 * by the next iteration. Also, decrement descriptor 282 * to compensate for the increment below the switch. 283 */ 284 descriptor--; 285 break; 286 } 287 case 'L': { 288 do { 289 descriptor++; 290 } while ((*descriptor != ';') && (*descriptor != '\0')); 291 count++; 292 if (*descriptor == '\0') { 293 /* Bogus descriptor. */ 294 done = true; 295 bogus = true; 296 } 297 break; 298 } 299 case ')': { 300 /* 301 * Note: The loop will exit after incrementing descriptor 302 * one more time, so it then points at the return type. 303 */ 304 done = true; 305 break; 306 } 307 default: { 308 /* Bogus descriptor. */ 309 done = true; 310 bogus = true; 311 break; 312 } 313 } 314 315 descriptor++; 316 } 317 318 if (bogus) { 319 *pReturnType = NULL; 320 return 0; 321 } 322 323 *pReturnType = descriptor; 324 return count; 325 } 326 327 /* 328 * Copy the argument types into the given array using the given buffer 329 * for the contents. 330 */ 331 static inline void copyTypes(char* buffer, const char** argTypes, 332 size_t argCount, const char* descriptor) 333 { 334 size_t i; 335 char c; 336 337 /* Skip the '('. */ 338 descriptor++; 339 340 for (i = 0; i < argCount; i++) { 341 argTypes[i] = buffer; 342 343 /* Copy all the array markers and one extra character. */ 344 do { 345 c = *(descriptor++); 346 *(buffer++) = c; 347 } while (c == '['); 348 349 if (c == 'L') { 350 /* Copy the rest of a class name. */ 351 do { 352 c = *(descriptor++); 353 *(buffer++) = c; 354 } while (c != ';'); 355 } 356 357 *(buffer++) = '\0'; 358 } 359 } 360 361 /* 362 * Look for a match in the given class. Returns the match if found 363 * or NULL if not. 364 */ 365 static Method* findMethodInListByDescriptor(const ClassObject* clazz, 366 bool findVirtual, bool isHier, const char* name, const char* descriptor) 367 { 368 const char* returnType; 369 size_t argCount = countArgsAndFindReturnType(descriptor, &returnType); 370 371 if (returnType == NULL) { 372 LOGW("Bogus method descriptor: %s", descriptor); 373 return NULL; 374 } 375 376 /* 377 * Make buffer big enough for all the argument type characters and 378 * one '\0' per argument. The "- 2" is because "returnType - 379 * descriptor" includes two parens. 380 */ 381 char buffer[argCount + (returnType - descriptor) - 2]; 382 const char* argTypes[argCount]; 383 384 copyTypes(buffer, argTypes, argCount, descriptor); 385 386 while (clazz != NULL) { 387 Method* methods; 388 size_t methodCount; 389 size_t i; 390 391 if (findVirtual) { 392 methods = clazz->virtualMethods; 393 methodCount = clazz->virtualMethodCount; 394 } else { 395 methods = clazz->directMethods; 396 methodCount = clazz->directMethodCount; 397 } 398 399 for (i = 0; i < methodCount; i++) { 400 Method* method = &methods[i]; 401 if (compareMethodHelper(method, name, returnType, argCount, 402 argTypes) == 0) { 403 return method; 404 } 405 } 406 407 if (! isHier) { 408 break; 409 } 410 411 clazz = clazz->super; 412 } 413 414 return NULL; 415 } 416 417 /* 418 * Look for a match in the given clazz. Returns the match if found 419 * or NULL if not. 420 * 421 * "wantedType" should be METHOD_VIRTUAL or METHOD_DIRECT to indicate the 422 * list to search through. If the match can come from either list, use 423 * MATCH_UNKNOWN to scan both. 424 */ 425 static Method* findMethodInListByProto(const ClassObject* clazz, 426 MethodType wantedType, bool isHier, const char* name, const DexProto* proto) 427 { 428 while (clazz != NULL) { 429 int i; 430 431 /* 432 * Check the virtual and/or direct method lists. 433 */ 434 if (wantedType == METHOD_VIRTUAL || wantedType == METHOD_UNKNOWN) { 435 for (i = 0; i < clazz->virtualMethodCount; i++) { 436 Method* method = &clazz->virtualMethods[i]; 437 if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) { 438 return method; 439 } 440 } 441 } 442 if (wantedType == METHOD_DIRECT || wantedType == METHOD_UNKNOWN) { 443 for (i = 0; i < clazz->directMethodCount; i++) { 444 Method* method = &clazz->directMethods[i]; 445 if (dvmCompareNameProtoAndMethod(name, proto, method) == 0) { 446 return method; 447 } 448 } 449 } 450 451 if (! isHier) { 452 break; 453 } 454 455 clazz = clazz->super; 456 } 457 458 return NULL; 459 } 460 461 /* 462 * Find a "virtual" method in a class. 463 * 464 * Does not chase into the superclass. 465 * 466 * Returns NULL if the method can't be found. (Does not throw an exception.) 467 */ 468 Method* dvmFindVirtualMethodByDescriptor(const ClassObject* clazz, 469 const char* methodName, const char* descriptor) 470 { 471 return findMethodInListByDescriptor(clazz, true, false, 472 methodName, descriptor); 473 474 // TODO? - throw IncompatibleClassChangeError if a match is 475 // found in the directMethods list, rather than NotFoundError. 476 // Note we could have been called by dvmFindVirtualMethodHier though. 477 } 478 479 480 /* 481 * Find a "virtual" method in a class, knowing only the name. This is 482 * only useful in limited circumstances, e.g. when searching for a member 483 * of an annotation class. 484 * 485 * Does not chase into the superclass. 486 * 487 * Returns NULL if the method can't be found. (Does not throw an exception.) 488 */ 489 Method* dvmFindVirtualMethodByName(const ClassObject* clazz, 490 const char* methodName) 491 { 492 Method* methods = clazz->virtualMethods; 493 int methodCount = clazz->virtualMethodCount; 494 int i; 495 496 for (i = 0; i < methodCount; i++) { 497 if (strcmp(methods[i].name, methodName) == 0) 498 return &methods[i]; 499 } 500 501 return NULL; 502 } 503 504 /* 505 * Find a "virtual" method in a class. 506 * 507 * Does not chase into the superclass. 508 * 509 * Returns NULL if the method can't be found. (Does not throw an exception.) 510 */ 511 Method* dvmFindVirtualMethod(const ClassObject* clazz, const char* methodName, 512 const DexProto* proto) 513 { 514 return findMethodInListByProto(clazz, METHOD_VIRTUAL, false, methodName, 515 proto); 516 } 517 518 /* 519 * Find a "virtual" method in a class. If we don't find it, try the 520 * superclass. Does not examine interfaces. 521 * 522 * Returns NULL if the method can't be found. (Does not throw an exception.) 523 */ 524 Method* dvmFindVirtualMethodHierByDescriptor(const ClassObject* clazz, 525 const char* methodName, const char* descriptor) 526 { 527 return findMethodInListByDescriptor(clazz, true, true, 528 methodName, descriptor); 529 } 530 531 /* 532 * Find a "virtual" method in a class. If we don't find it, try the 533 * superclass. Does not examine interfaces. 534 * 535 * Returns NULL if the method can't be found. (Does not throw an exception.) 536 */ 537 Method* dvmFindVirtualMethodHier(const ClassObject* clazz, 538 const char* methodName, const DexProto* proto) 539 { 540 return findMethodInListByProto(clazz, METHOD_VIRTUAL, true, methodName, 541 proto); 542 } 543 544 /* 545 * Find a method in an interface. Searches superinterfaces. 546 * 547 * Returns NULL if the method can't be found. (Does not throw an exception.) 548 */ 549 Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface, 550 const char* methodName, const char* descriptor) 551 { 552 Method* resMethod = dvmFindVirtualMethodByDescriptor(iface, 553 methodName, descriptor); 554 if (resMethod == NULL) { 555 /* scan superinterfaces and superclass interfaces */ 556 int i; 557 for (i = 0; i < iface->iftableCount; i++) { 558 resMethod = dvmFindVirtualMethodByDescriptor(iface->iftable[i].clazz, 559 methodName, descriptor); 560 if (resMethod != NULL) 561 break; 562 } 563 } 564 return resMethod; 565 } 566 567 /* 568 * Find a method in an interface. Searches superinterfaces. 569 * 570 * Returns NULL if the method can't be found. (Does not throw an exception.) 571 */ 572 Method* dvmFindInterfaceMethodHier(const ClassObject* iface, 573 const char* methodName, const DexProto* proto) 574 { 575 Method* resMethod = dvmFindVirtualMethod(iface, methodName, proto); 576 if (resMethod == NULL) { 577 /* scan superinterfaces and superclass interfaces */ 578 int i; 579 for (i = 0; i < iface->iftableCount; i++) { 580 resMethod = dvmFindVirtualMethod(iface->iftable[i].clazz, 581 methodName, proto); 582 if (resMethod != NULL) 583 break; 584 } 585 } 586 return resMethod; 587 } 588 589 /* 590 * Find a "direct" method (static, private, or "<*init>"). 591 * 592 * Returns NULL if the method can't be found. (Does not throw an exception.) 593 */ 594 Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz, 595 const char* methodName, const char* descriptor) 596 { 597 return findMethodInListByDescriptor(clazz, false, false, 598 methodName, descriptor); 599 } 600 601 /* 602 * Find a "direct" method. If we don't find it, try the superclass. This 603 * is only appropriate for static methods, but will work for all direct 604 * methods. 605 * 606 * Returns NULL if the method can't be found. (Does not throw an exception.) 607 */ 608 Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz, 609 const char* methodName, const char* descriptor) 610 { 611 return findMethodInListByDescriptor(clazz, false, true, 612 methodName, descriptor); 613 } 614 615 /* 616 * Find a "direct" method (static or "<*init>"). 617 * 618 * Returns NULL if the method can't be found. (Does not throw an exception.) 619 */ 620 Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName, 621 const DexProto* proto) 622 { 623 return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName, 624 proto); 625 } 626 627 /* 628 * Find a "direct" method in a class. If we don't find it, try the 629 * superclass. 630 * 631 * Returns NULL if the method can't be found. (Does not throw an exception.) 632 */ 633 Method* dvmFindDirectMethodHier(const ClassObject* clazz, 634 const char* methodName, const DexProto* proto) 635 { 636 return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName, 637 proto); 638 } 639 640 /* 641 * Find a virtual or static method in a class. If we don't find it, try the 642 * superclass. This is compatible with the VM spec (v2 5.4.3.3) method 643 * search order, but it stops short of scanning through interfaces (which 644 * should be done after this function completes). 645 * 646 * In most cases we know that we're looking for either a static or an 647 * instance field and there's no value in searching through both types. 648 * During verification we need to recognize and reject certain unusual 649 * situations, and we won't see them unless we walk the lists this way. 650 * 651 * Returns NULL if the method can't be found. (Does not throw an exception.) 652 */ 653 Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName, 654 const DexProto* proto) 655 { 656 return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName, 657 proto); 658 } 659 660 661 /* 662 * We have a method pointer for a method in "clazz", but it might be 663 * pointing to a method in a derived class. We want to find the actual entry 664 * from the class' vtable. If "clazz" is an interface, we have to do a 665 * little more digging. 666 * 667 * For "direct" methods (private / constructor), we just return the 668 * original Method. 669 * 670 * (This is used for reflection and JNI "call method" calls.) 671 */ 672 const Method* dvmGetVirtualizedMethod(const ClassObject* clazz, 673 const Method* meth) 674 { 675 Method* actualMeth; 676 int methodIndex; 677 678 if (dvmIsDirectMethod(meth)) { 679 /* no vtable entry for these */ 680 assert(!dvmIsStaticMethod(meth)); 681 return meth; 682 } 683 684 /* 685 * If the method was declared in an interface, we need to scan through 686 * the class' list of interfaces for it, and find the vtable index 687 * from that. 688 * 689 * TODO: use the interface cache. 690 */ 691 if (dvmIsInterfaceClass(meth->clazz)) { 692 int i; 693 694 for (i = 0; i < clazz->iftableCount; i++) { 695 if (clazz->iftable[i].clazz == meth->clazz) 696 break; 697 } 698 if (i == clazz->iftableCount) { 699 dvmThrowIncompatibleClassChangeError( 700 "invoking method from interface not implemented by class"); 701 return NULL; 702 } 703 704 methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex]; 705 } else { 706 methodIndex = meth->methodIndex; 707 } 708 709 assert(methodIndex >= 0 && methodIndex < clazz->vtableCount); 710 actualMeth = clazz->vtable[methodIndex]; 711 712 /* 713 * Make sure there's code to execute. 714 */ 715 if (dvmIsAbstractMethod(actualMeth)) { 716 dvmThrowAbstractMethodError(NULL); 717 return NULL; 718 } 719 assert(!dvmIsMirandaMethod(actualMeth)); 720 721 return actualMeth; 722 } 723 724 /* 725 * Get the source file for a method. 726 */ 727 const char* dvmGetMethodSourceFile(const Method* meth) 728 { 729 /* 730 * TODO: A method's debug info can override the default source 731 * file for a class, so we should account for that possibility 732 * here. 733 */ 734 return meth->clazz->sourceFile; 735 } 736 737 /* 738 * Dump some information about an object. 739 */ 740 void dvmDumpObject(const Object* obj) 741 { 742 ClassObject* clazz; 743 int i; 744 745 if (obj == NULL || obj->clazz == NULL) { 746 LOGW("Null or malformed object not dumped"); 747 return; 748 } 749 750 clazz = obj->clazz; 751 LOGD("----- Object dump: %p (%s, %d bytes) -----", 752 obj, clazz->descriptor, (int) clazz->objectSize); 753 //printHexDump(obj, clazz->objectSize); 754 LOGD(" Fields:"); 755 while (clazz != NULL) { 756 LOGD(" -- %s", clazz->descriptor); 757 for (i = 0; i < clazz->ifieldCount; i++) { 758 const InstField* pField = &clazz->ifields[i]; 759 char type = pField->signature[0]; 760 761 if (type == 'F' || type == 'D') { 762 double dval; 763 764 if (type == 'F') 765 dval = dvmGetFieldFloat(obj, pField->byteOffset); 766 else 767 dval = dvmGetFieldDouble(obj, pField->byteOffset); 768 769 LOGD(" %2d: '%s' '%s' af=%04x off=%d %.3f", i, 770 pField->name, pField->signature, 771 pField->accessFlags, pField->byteOffset, dval); 772 } else { 773 u8 lval; 774 775 if (type == 'J') 776 lval = dvmGetFieldLong(obj, pField->byteOffset); 777 else if (type == 'Z') 778 lval = dvmGetFieldBoolean(obj, pField->byteOffset); 779 else 780 lval = dvmGetFieldInt(obj, pField->byteOffset); 781 782 LOGD(" %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i, 783 pField->name, pField->signature, 784 pField->accessFlags, pField->byteOffset, lval); 785 } 786 } 787 788 clazz = clazz->super; 789 } 790 if (dvmIsClassObject(obj)) { 791 LOGD(" Static fields:"); 792 const StaticField* sfields = &((ClassObject *)obj)->sfields[0]; 793 for (i = 0; i < ((ClassObject *)obj)->sfieldCount; ++i) { 794 const StaticField* pField = &sfields[i]; 795 size_t byteOffset = (size_t)pField - (size_t)sfields; 796 char type = pField->signature[0]; 797 798 if (type == 'F' || type == 'D') { 799 double dval; 800 801 if (type == 'F') 802 dval = pField->value.f; 803 else 804 dval = pField->value.d; 805 806 LOGD(" %2d: '%s' '%s' af=%04x off=%zd %.3f", i, 807 pField->name, pField->signature, 808 pField->accessFlags, byteOffset, dval); 809 } else { 810 u8 lval; 811 812 if (type == 'J') 813 lval = pField->value.j; 814 else if (type == 'Z') 815 lval = pField->value.z; 816 else 817 lval = pField->value.i; 818 819 LOGD(" %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i, 820 pField->name, pField->signature, 821 pField->accessFlags, byteOffset, lval); 822 } 823 } 824 } 825 } 826