• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
dvmFindInstanceField(const ClassObject * clazz,const char * fieldName,const char * signature)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->field.name) == 0 &&
44             strcmp(signature, pField->field.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  */
dvmFindInstanceFieldHier(const ClassObject * clazz,const char * fieldName,const char * signature)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  */
dvmFindStaticField(const ClassObject * clazz,const char * fieldName,const char * signature)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->field.name) == 0 &&
101             strcmp(signature, pField->field.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  */
dvmFindStaticFieldHier(const ClassObject * clazz,const char * fieldName,const char * signature)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  */
dvmFindFieldHier(const ClassObject * clazz,const char * fieldName,const char * signature)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  */
compareMethodHelper(Method * method,const char * methodName,const char * returnType,size_t argCount,const char ** argTypes)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  */
countArgsAndFindReturnType(const char * descriptor,const char ** pReturnType)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  */
copyTypes(char * buffer,const char ** argTypes,size_t argCount,const char * descriptor)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  */
findMethodInListByDescriptor(const ClassObject * clazz,bool findVirtual,bool isHier,const char * name,const char * descriptor)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\n", 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  */
findMethodInListByProto(const ClassObject * clazz,MethodType wantedType,bool isHier,const char * name,const DexProto * proto)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  */
dvmFindVirtualMethodByDescriptor(const ClassObject * clazz,const char * methodName,const char * descriptor)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  */
dvmFindVirtualMethodByName(const ClassObject * clazz,const char * methodName)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  */
dvmFindVirtualMethod(const ClassObject * clazz,const char * methodName,const DexProto * proto)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.
521  *
522  * Returns NULL if the method can't be found.  (Does not throw an exception.)
523  */
dvmFindVirtualMethodHierByDescriptor(const ClassObject * clazz,const char * methodName,const char * descriptor)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.
534  *
535  * Returns NULL if the method can't be found.  (Does not throw an exception.)
536  */
dvmFindVirtualMethodHier(const ClassObject * clazz,const char * methodName,const DexProto * proto)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 "direct" method (static, private, or "<*init>").
546  *
547  * Returns NULL if the method can't be found.  (Does not throw an exception.)
548  */
dvmFindDirectMethodByDescriptor(const ClassObject * clazz,const char * methodName,const char * descriptor)549 Method* dvmFindDirectMethodByDescriptor(const ClassObject* clazz,
550     const char* methodName, const char* descriptor)
551 {
552     return findMethodInListByDescriptor(clazz, false, false,
553             methodName, descriptor);
554 }
555 
556 /*
557  * Find a "direct" method.  If we don't find it, try the superclass.  This
558  * is only appropriate for static methods, but will work for all direct
559  * methods.
560  *
561  * Returns NULL if the method can't be found.  (Does not throw an exception.)
562  */
dvmFindDirectMethodHierByDescriptor(const ClassObject * clazz,const char * methodName,const char * descriptor)563 Method* dvmFindDirectMethodHierByDescriptor(const ClassObject* clazz,
564     const char* methodName, const char* descriptor)
565 {
566     return findMethodInListByDescriptor(clazz, false, true,
567             methodName, descriptor);
568 }
569 
570 /*
571  * Find a "direct" method (static or "<*init>").
572  *
573  * Returns NULL if the method can't be found.  (Does not throw an exception.)
574  */
dvmFindDirectMethod(const ClassObject * clazz,const char * methodName,const DexProto * proto)575 Method* dvmFindDirectMethod(const ClassObject* clazz, const char* methodName,
576     const DexProto* proto)
577 {
578     return findMethodInListByProto(clazz, METHOD_DIRECT, false, methodName,
579             proto);
580 }
581 
582 /*
583  * Find a "direct" method in a class.  If we don't find it, try the
584  * superclass.
585  *
586  * Returns NULL if the method can't be found.  (Does not throw an exception.)
587  */
dvmFindDirectMethodHier(const ClassObject * clazz,const char * methodName,const DexProto * proto)588 Method* dvmFindDirectMethodHier(const ClassObject* clazz,
589     const char* methodName, const DexProto* proto)
590 {
591     return findMethodInListByProto(clazz, METHOD_DIRECT, true, methodName,
592             proto);
593 }
594 
595 /*
596  * Find a virtual or static method in a class.  If we don't find it, try the
597  * superclass.  This is compatible with the VM spec (v2 5.4.3.3) method
598  * search order, but it stops short of scanning through interfaces (which
599  * should be done after this function completes).
600  *
601  * In most cases we know that we're looking for either a static or an
602  * instance field and there's no value in searching through both types.
603  * During verification we need to recognize and reject certain unusual
604  * situations, and we won't see them unless we walk the lists this way.
605  *
606  * Returns NULL if the method can't be found.  (Does not throw an exception.)
607  */
dvmFindMethodHier(const ClassObject * clazz,const char * methodName,const DexProto * proto)608 Method* dvmFindMethodHier(const ClassObject* clazz, const char* methodName,
609     const DexProto* proto)
610 {
611     return findMethodInListByProto(clazz, METHOD_UNKNOWN, true, methodName,
612             proto);
613 }
614 
615 
616 /*
617  * We have a method pointer for a method in "clazz", but it might be
618  * pointing to a method in a derived class.  We want to find the actual entry
619  * from the class' vtable.  If "clazz" is an interface, we have to do a
620  * little more digging.
621  *
622  * (This is used for reflection and JNI "call method" calls.)
623  */
dvmGetVirtualizedMethod(const ClassObject * clazz,const Method * meth)624 const Method* dvmGetVirtualizedMethod(const ClassObject* clazz,
625     const Method* meth)
626 {
627     Method* actualMeth;
628     int methodIndex;
629 
630     assert(!dvmIsStaticMethod(meth));
631 
632     if (dvmIsPrivateMethod(meth))   // no vtable entry for these
633         return meth;
634 
635     /*
636      * If the method was declared in an interface, we need to scan through
637      * the class' list of interfaces for it, and find the vtable index
638      * from that.
639      *
640      * TODO: use the interface cache.
641      */
642     if (dvmIsInterfaceClass(meth->clazz)) {
643         int i;
644 
645         for (i = 0; i < clazz->iftableCount; i++) {
646             if (clazz->iftable[i].clazz == meth->clazz)
647                 break;
648         }
649         if (i == clazz->iftableCount) {
650             dvmThrowException("Ljava/lang/IncompatibleClassChangeError;",
651                 "invoking method from interface not implemented by class");
652             return NULL;
653         }
654 
655         methodIndex = clazz->iftable[i].methodIndexArray[meth->methodIndex];
656     } else {
657         methodIndex = meth->methodIndex;
658     }
659 
660     assert(methodIndex >= 0 && methodIndex < clazz->vtableCount);
661     actualMeth = clazz->vtable[methodIndex];
662 
663     /*
664      * Make sure there's code to execute.
665      */
666     if (dvmIsAbstractMethod(actualMeth)) {
667         dvmThrowException("Ljava/lang/AbstractMethodError;", NULL);
668         return NULL;
669     }
670     assert(!dvmIsMirandaMethod(actualMeth));
671 
672     return actualMeth;
673 }
674 
675 /*
676  * Get the source file for a method.
677  */
dvmGetMethodSourceFile(const Method * meth)678 const char* dvmGetMethodSourceFile(const Method* meth)
679 {
680     /*
681      * TODO: A method's debug info can override the default source
682      * file for a class, so we should account for that possibility
683      * here.
684      */
685     return meth->clazz->sourceFile;
686 }
687 
688 /*
689  * Dump some information about an object.
690  */
dvmDumpObject(const Object * obj)691 void dvmDumpObject(const Object* obj)
692 {
693     ClassObject* clazz;
694     int i;
695 
696     if (obj == NULL || obj->clazz == NULL) {
697         LOGW("Null or malformed object not dumped");
698         return;
699     }
700 
701     clazz = obj->clazz;
702     LOGD("----- Object dump: %p (%s, %d bytes) -----",
703         obj, clazz->descriptor, (int) clazz->objectSize);
704     //printHexDump(obj, clazz->objectSize);
705     LOGD("  Fields:");
706     while (clazz != NULL) {
707         LOGD("    -- %s", clazz->descriptor);
708         for (i = 0; i < clazz->ifieldCount; i++) {
709             const InstField* pField = &clazz->ifields[i];
710             char type = pField->field.signature[0];
711 
712             if (type == 'F' || type == 'D') {
713                 double dval;
714 
715                 if (type == 'F')
716                     dval = dvmGetFieldFloat(obj, pField->byteOffset);
717                 else
718                     dval = dvmGetFieldDouble(obj, pField->byteOffset);
719 
720                 LOGD("    %2d: '%s' '%s' af=%04x off=%d %.3f", i,
721                     pField->field.name, pField->field.signature,
722                     pField->field.accessFlags, pField->byteOffset, dval);
723             } else {
724                 u8 lval;
725 
726                 if (type == 'J')
727                     lval = dvmGetFieldLong(obj, pField->byteOffset);
728                 else if (type == 'Z')
729                     lval = dvmGetFieldBoolean(obj, pField->byteOffset);
730                 else
731                     lval = dvmGetFieldInt(obj, pField->byteOffset);
732 
733                 LOGD("    %2d: '%s' '%s' af=%04x off=%d 0x%08llx", i,
734                     pField->field.name, pField->field.signature,
735                     pField->field.accessFlags, pField->byteOffset, lval);
736             }
737         }
738 
739         clazz = clazz->super;
740     }
741     if (obj->clazz == gDvm.classJavaLangClass) {
742         LOGD("  Static fields:");
743         const StaticField* sfields = &((ClassObject *)obj)->sfields[0];
744         for (i = 0; i < ((ClassObject *)obj)->sfieldCount; ++i) {
745             const StaticField* pField = &sfields[i];
746             size_t byteOffset = (size_t)pField - (size_t)sfields;
747             char type = pField->field.signature[0];
748 
749             if (type == 'F' || type == 'D') {
750                 double dval;
751 
752                 if (type == 'F')
753                     dval = pField->value.f;
754                 else
755                     dval = pField->value.d;
756 
757                 LOGD("    %2d: '%s' '%s' af=%04x off=%zd %.3f", i,
758                      pField->field.name, pField->field.signature,
759                      pField->field.accessFlags, byteOffset, dval);
760             } else {
761                 u8 lval;
762 
763                 if (type == 'J')
764                     lval = pField->value.j;
765                 else if (type == 'Z')
766                     lval = pField->value.z;
767                 else
768                     lval = pField->value.i;
769 
770                 LOGD("    %2d: '%s' '%s' af=%04x off=%zd 0x%08llx", i,
771                      pField->field.name, pField->field.signature,
772                      pField->field.accessFlags, byteOffset, lval);
773             }
774         }
775     }
776 }
777