Object.cpp revision 60fc806b679a3655c228b4093058c59941a49cfe
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 */
27InstField* 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 */
61InstField* 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 */
85StaticField* 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 */
115StaticField* 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 */
165Field* 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 */
208static 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 */
258static 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 */
331static 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 */
365static 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 */
425static 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 */
468Method* 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 */
489Method* 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 */
511Method* 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 */
524Method* 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 */
537Method* 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 */
549Method* 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 */
572Method* 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 */
594Method* 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 */
608Method* 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 */
620Method* 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 */
633Method* 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 */
653Method* 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 */
672const 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 */
727const 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 */
740void 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