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 * Functions for dealing with method prototypes
19 */
20
21#include "DexProto.h"
22
23#include <stdlib.h>
24#include <string.h>
25
26/*
27 * ===========================================================================
28 *      String Cache
29 * ===========================================================================
30 */
31
32/*
33 * Make sure that the given cache can hold a string of the given length,
34 * including the final '\0' byte.
35 */
36void dexStringCacheAlloc(DexStringCache* pCache, size_t length) {
37    if (pCache->allocatedSize != 0) {
38        if (pCache->allocatedSize >= length) {
39            return;
40        }
41        free((void*) pCache->value);
42    }
43
44    if (length <= sizeof(pCache->buffer)) {
45        pCache->value = pCache->buffer;
46        pCache->allocatedSize = 0;
47    } else {
48        pCache->value = (char*) malloc(length);
49        pCache->allocatedSize = length;
50    }
51}
52
53/*
54 * Initialize the given DexStringCache. Use this function before passing
55 * one into any other function.
56 */
57void dexStringCacheInit(DexStringCache* pCache) {
58    pCache->value = pCache->buffer;
59    pCache->allocatedSize = 0;
60    pCache->buffer[0] = '\0';
61}
62
63/*
64 * Release the allocated contents of the given DexStringCache, if any.
65 * Use this function after your last use of a DexStringCache.
66 */
67void dexStringCacheRelease(DexStringCache* pCache) {
68    if (pCache->allocatedSize != 0) {
69        free((void*) pCache->value);
70        pCache->value = pCache->buffer;
71        pCache->allocatedSize = 0;
72    }
73}
74
75/*
76 * If the given DexStringCache doesn't already point at the given value,
77 * make a copy of it into the cache. This always returns a writable
78 * pointer to the contents (whether or not a copy had to be made). This
79 * function is intended to be used after making a call that at least
80 * sometimes doesn't populate a DexStringCache.
81 */
82char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value) {
83    if (value != pCache->value) {
84        size_t length = strlen(value) + 1;
85        dexStringCacheAlloc(pCache, length);
86        memcpy(pCache->value, value, length);
87    }
88
89    return pCache->value;
90}
91
92/*
93 * Abandon the given DexStringCache, and return a writable copy of the
94 * given value (reusing the string cache's allocation if possible).
95 * The return value must be free()d by the caller. Use this instead of
96 * dexStringCacheRelease() if you want the buffer to survive past the
97 * scope of the DexStringCache.
98 */
99char* dexStringCacheAbandon(DexStringCache* pCache, const char* value) {
100    if ((value == pCache->value) && (pCache->allocatedSize != 0)) {
101        char* result = pCache->value;
102        pCache->allocatedSize = 0;
103        pCache->value = pCache->buffer;
104        return result;
105    } else {
106        return strdup(value);
107    }
108}
109
110
111/*
112 * ===========================================================================
113 *      Method Prototypes
114 * ===========================================================================
115 */
116
117/*
118 * Return the DexProtoId from the given DexProto. The DexProto must
119 * actually refer to a DexProtoId.
120 */
121static inline const DexProtoId* getProtoId(const DexProto* pProto) {
122    return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
123}
124
125/* (documented in header file) */
126const char* dexProtoGetShorty(const DexProto* pProto) {
127    const DexProtoId* protoId = getProtoId(pProto);
128
129    return dexStringById(pProto->dexFile, protoId->shortyIdx);
130}
131
132/* (documented in header file) */
133const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
134        DexStringCache* pCache) {
135    const DexFile* dexFile = pProto->dexFile;
136    const DexProtoId* protoId = getProtoId(pProto);
137    const DexTypeList* typeList = dexGetProtoParameters(dexFile, protoId);
138    size_t length = 3; // parens and terminating '\0'
139    u4 paramCount = (typeList == NULL) ? 0 : typeList->size;
140    u4 i;
141
142    for (i = 0; i < paramCount; i++) {
143        u4 idx = dexTypeListGetIdx(typeList, i);
144        length += strlen(dexStringByTypeIdx(dexFile, idx));
145    }
146
147    length += strlen(dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
148
149    dexStringCacheAlloc(pCache, length);
150
151    char *at = (char*) pCache->value;
152    *(at++) = '(';
153
154    for (i = 0; i < paramCount; i++) {
155        u4 idx = dexTypeListGetIdx(typeList, i);
156        const char* desc = dexStringByTypeIdx(dexFile, idx);
157        strcpy(at, desc);
158        at += strlen(desc);
159    }
160
161    *(at++) = ')';
162
163    strcpy(at, dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
164    return pCache->value;
165}
166
167/* (documented in header file) */
168char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
169    DexStringCache cache;
170
171    dexStringCacheInit(&cache);
172    return dexStringCacheAbandon(&cache,
173            dexProtoGetMethodDescriptor(pProto, &cache));
174}
175
176/* (documented in header file) */
177const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
178        DexStringCache* pCache) {
179    DexParameterIterator iterator;
180    size_t length = 1; /* +1 for the terminating '\0' */
181
182    dexParameterIteratorInit(&iterator, pProto);
183
184    for (;;) {
185        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
186        if (descriptor == NULL) {
187            break;
188        }
189
190        length += strlen(descriptor);
191    }
192
193    dexParameterIteratorInit(&iterator, pProto);
194
195    dexStringCacheAlloc(pCache, length);
196    char *at = (char*) pCache->value;
197
198    for (;;) {
199        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
200        if (descriptor == NULL) {
201            break;
202        }
203
204        strcpy(at, descriptor);
205        at += strlen(descriptor);
206    }
207
208    return pCache->value;
209}
210
211/* (documented in header file) */
212const char* dexProtoGetReturnType(const DexProto* pProto) {
213    const DexProtoId* protoId = getProtoId(pProto);
214    return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
215}
216
217/* (documented in header file) */
218size_t dexProtoGetParameterCount(const DexProto* pProto) {
219    const DexProtoId* protoId = getProtoId(pProto);
220    const DexTypeList* typeList =
221        dexGetProtoParameters(pProto->dexFile, protoId);
222    return (typeList == NULL) ? 0 : typeList->size;
223}
224
225/* (documented in header file) */
226int dexProtoComputeArgsSize(const DexProto* pProto) {
227    const char* shorty = dexProtoGetShorty(pProto);
228    int count = 0;
229
230    /* Skip the return type. */
231    shorty++;
232
233    for (;;) {
234        switch (*(shorty++)) {
235            case '\0': {
236                return count;
237            }
238            case 'D':
239            case 'J': {
240                count += 2;
241                break;
242            }
243            default: {
244                count++;
245                break;
246            }
247        }
248    }
249}
250
251/*
252 * Common implementation for dexProtoCompare() and dexProtoCompareParameters().
253 */
254static int protoCompare(const DexProto* pProto1, const DexProto* pProto2,
255        bool compareReturnType) {
256
257    if (pProto1 == pProto2) {
258        // Easy out.
259        return 0;
260    } else {
261        const DexFile* dexFile1 = pProto1->dexFile;
262        const DexProtoId* protoId1 = getProtoId(pProto1);
263        const DexTypeList* typeList1 =
264            dexGetProtoParameters(dexFile1, protoId1);
265        int paramCount1 = (typeList1 == NULL) ? 0 : typeList1->size;
266
267        const DexFile* dexFile2 = pProto2->dexFile;
268        const DexProtoId* protoId2 = getProtoId(pProto2);
269        const DexTypeList* typeList2 =
270            dexGetProtoParameters(dexFile2, protoId2);
271        int paramCount2 = (typeList2 == NULL) ? 0 : typeList2->size;
272
273        if (protoId1 == protoId2) {
274            // Another easy out.
275            return 0;
276        }
277
278        // Compare return types.
279
280        if (compareReturnType) {
281            int result =
282                strcmp(dexStringByTypeIdx(dexFile1, protoId1->returnTypeIdx),
283                        dexStringByTypeIdx(dexFile2, protoId2->returnTypeIdx));
284
285            if (result != 0) {
286                return result;
287            }
288        }
289
290        // Compare parameters.
291
292        int minParam = (paramCount1 > paramCount2) ? paramCount2 : paramCount1;
293        int i;
294
295        for (i = 0; i < minParam; i++) {
296            u4 idx1 = dexTypeListGetIdx(typeList1, i);
297            u4 idx2 = dexTypeListGetIdx(typeList2, i);
298            int result =
299                strcmp(dexStringByTypeIdx(dexFile1, idx1),
300                        dexStringByTypeIdx(dexFile2, idx2));
301
302            if (result != 0) {
303                return result;
304            }
305        }
306
307        if (paramCount1 < paramCount2) {
308            return -1;
309        } else if (paramCount1 > paramCount2) {
310            return 1;
311        } else {
312            return 0;
313        }
314    }
315}
316
317/* (documented in header file) */
318int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
319    return protoCompare(pProto1, pProto2, true);
320}
321
322/* (documented in header file) */
323int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2){
324    return protoCompare(pProto1, pProto2, false);
325}
326
327
328/*
329 * Helper for dexProtoCompareToDescriptor(), which gets the return type
330 * descriptor from a method descriptor string.
331 */
332static const char* methodDescriptorReturnType(const char* descriptor) {
333    const char* result = strchr(descriptor, ')');
334
335    if (result == NULL) {
336        return NULL;
337    }
338
339    // The return type is the character just past the ')'.
340    return result + 1;
341}
342
343/*
344 * Helper for dexProtoCompareToDescriptor(), which indicates the end
345 * of an embedded argument type descriptor, which is also the
346 * beginning of the next argument type descriptor. Since this is for
347 * argument types, it doesn't accept 'V' as a valid type descriptor.
348 */
349static const char* methodDescriptorNextType(const char* descriptor) {
350    // Skip any array references.
351
352    while (*descriptor == '[') {
353        descriptor++;
354    }
355
356    switch (*descriptor) {
357        case 'B': case 'C': case 'D': case 'F':
358        case 'I': case 'J': case 'S': case 'Z': {
359            return descriptor + 1;
360        }
361        case 'L': {
362            const char* result = strchr(descriptor + 1, ';');
363            if (result != NULL) {
364                // The type ends just past the ';'.
365                return result + 1;
366            }
367        }
368    }
369
370    return NULL;
371}
372
373/*
374 * Common implementation for dexProtoCompareToDescriptor() and
375 * dexProtoCompareToParameterDescriptors(). The descriptor argument
376 * can be either a full method descriptor (with parens and a return
377 * type) or an unadorned concatenation of types (e.g. a list of
378 * argument types).
379 */
380static int protoCompareToParameterDescriptors(const DexProto* proto,
381        const char* descriptor, bool expectParens) {
382    char expectedEndChar = expectParens ? ')' : '\0';
383    DexParameterIterator iterator;
384    dexParameterIteratorInit(&iterator, proto);
385
386    if (expectParens) {
387        // Skip the '('.
388        assert (*descriptor == '(');
389        descriptor++;
390    }
391
392    for (;;) {
393        const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
394
395        if (*descriptor == expectedEndChar) {
396            // It's the end of the descriptor string.
397            if (protoDesc == NULL) {
398                // It's also the end of the prototype's arguments.
399                return 0;
400            } else {
401                // The prototype still has more arguments.
402                return 1;
403            }
404        }
405
406        if (protoDesc == NULL) {
407            /*
408             * The prototype doesn't have arguments left, but the
409             * descriptor string does.
410             */
411            return -1;
412        }
413
414        // Both prototype and descriptor have arguments. Compare them.
415
416        const char* nextDesc = methodDescriptorNextType(descriptor);
417        assert(nextDesc != NULL);
418
419        for (;;) {
420            char c1 = *(protoDesc++);
421            char c2 = (descriptor < nextDesc) ? *(descriptor++) : '\0';
422
423            if (c1 < c2) {
424                // This includes the case where the proto is shorter.
425                return -1;
426            } else if (c1 > c2) {
427                // This includes the case where the desc is shorter.
428                return 1;
429            } else if (c1 == '\0') {
430                // The two types are equal in length. (c2 necessarily == '\0'.)
431                break;
432            }
433        }
434
435        /*
436         * If we made it here, the two arguments matched, and
437         * descriptor == nextDesc.
438         */
439    }
440}
441
442/* (documented in header file) */
443int dexProtoCompareToDescriptor(const DexProto* proto,
444        const char* descriptor) {
445    // First compare the return types.
446
447    const char *returnType = methodDescriptorReturnType(descriptor);
448    assert(returnType != NULL);
449
450    int result = strcmp(dexProtoGetReturnType(proto), returnType);
451
452    if (result != 0) {
453        return result;
454    }
455
456    // The return types match, so we have to check arguments.
457    return protoCompareToParameterDescriptors(proto, descriptor, true);
458}
459
460/* (documented in header file) */
461int dexProtoCompareToParameterDescriptors(const DexProto* proto,
462        const char* descriptors) {
463    return protoCompareToParameterDescriptors(proto, descriptors, false);
464}
465
466
467
468
469
470
471/*
472 * ===========================================================================
473 *      Parameter Iterators
474 * ===========================================================================
475 */
476
477/*
478 * Initialize the given DexParameterIterator to be at the start of the
479 * parameters of the given prototype.
480 */
481void dexParameterIteratorInit(DexParameterIterator* pIterator,
482        const DexProto* pProto) {
483    pIterator->proto = pProto;
484    pIterator->cursor = 0;
485
486    pIterator->parameters =
487        dexGetProtoParameters(pProto->dexFile, getProtoId(pProto));
488    pIterator->parameterCount = (pIterator->parameters == NULL) ? 0
489        : pIterator->parameters->size;
490}
491
492/*
493 * Get the type_id index for the next parameter, if any. This returns
494 * kDexNoIndex if the last parameter has already been consumed.
495 */
496u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator) {
497    int cursor = pIterator->cursor;
498    int parameterCount = pIterator->parameterCount;
499
500    if (cursor >= parameterCount) {
501        // The iteration is complete.
502        return kDexNoIndex;
503    } else {
504        u4 idx = dexTypeListGetIdx(pIterator->parameters, cursor);
505        pIterator->cursor++;
506        return idx;
507    }
508}
509
510/*
511 * Get the type descriptor for the next parameter, if any. This returns
512 * NULL if the last parameter has already been consumed.
513 */
514const char* dexParameterIteratorNextDescriptor(
515        DexParameterIterator* pIterator) {
516    u4 idx = dexParameterIteratorNextIndex(pIterator);
517
518    if (idx == kDexNoIndex) {
519        return NULL;
520    }
521
522    return dexStringByTypeIdx(pIterator->proto->dexFile, idx);
523}
524