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 */
36static void 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 = 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/*
126 * Get the short-form method descriptor for the given prototype. The
127 * prototype must be protoIdx-based.
128 */
129const char* dexProtoGetShorty(const DexProto* pProto) {
130    const DexProtoId* protoId = getProtoId(pProto);
131
132    return dexStringById(pProto->dexFile, protoId->shortyIdx);
133}
134
135/*
136 * Get the full method descriptor for the given prototype.
137 */
138const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
139        DexStringCache* pCache) {
140    const DexFile* dexFile = pProto->dexFile;
141    const DexProtoId* protoId = getProtoId(pProto);
142    const DexTypeList* typeList = dexGetProtoParameters(dexFile, protoId);
143    size_t length = 3; // parens and terminating '\0'
144    u4 paramCount = (typeList == NULL) ? 0 : typeList->size;
145    u4 i;
146
147    for (i = 0; i < paramCount; i++) {
148        u4 idx = dexTypeListGetIdx(typeList, i);
149        length += strlen(dexStringByTypeIdx(dexFile, idx));
150    }
151
152    length += strlen(dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
153
154    dexStringCacheAlloc(pCache, length);
155
156    char *at = (char*) pCache->value;
157    *(at++) = '(';
158
159    for (i = 0; i < paramCount; i++) {
160        u4 idx = dexTypeListGetIdx(typeList, i);
161        const char* desc = dexStringByTypeIdx(dexFile, idx);
162        strcpy(at, desc);
163        at += strlen(desc);
164    }
165
166    *(at++) = ')';
167
168    strcpy(at, dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
169    return pCache->value;
170}
171
172/*
173 * Get a copy of the descriptor string associated with the given prototype.
174 * The returned pointer must be free()ed by the caller.
175 */
176char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
177    DexStringCache cache;
178
179    dexStringCacheInit(&cache);
180    return dexStringCacheAbandon(&cache,
181            dexProtoGetMethodDescriptor(pProto, &cache));
182}
183
184/*
185 * Get the parameter descriptors for the given prototype. This is the
186 * concatenation of all the descriptors for all the parameters, in
187 * order, with no other adornment.
188 */
189const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
190        DexStringCache* pCache) {
191    DexParameterIterator iterator;
192    size_t length = 1; /* +1 for the terminating '\0' */
193
194    dexParameterIteratorInit(&iterator, pProto);
195
196    for (;;) {
197        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
198        if (descriptor == NULL) {
199            break;
200        }
201
202        length += strlen(descriptor);
203    }
204
205    dexParameterIteratorInit(&iterator, pProto);
206
207    dexStringCacheAlloc(pCache, length);
208    char *at = (char*) pCache->value;
209
210    for (;;) {
211        const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
212        if (descriptor == NULL) {
213            break;
214        }
215
216        strcpy(at, descriptor);
217        at += strlen(descriptor);
218    }
219
220    return pCache->value;
221}
222
223/*
224 * Get the type descriptor for the return type of the given prototype.
225 */
226const char* dexProtoGetReturnType(const DexProto* pProto) {
227    const DexProtoId* protoId = getProtoId(pProto);
228    return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
229}
230
231/*
232 * Get the parameter count of the given prototype.
233 */
234size_t dexProtoGetParameterCount(const DexProto* pProto) {
235    const DexProtoId* protoId = getProtoId(pProto);
236    const DexTypeList* typeList =
237        dexGetProtoParameters(pProto->dexFile, protoId);
238    return (typeList == NULL) ? 0 : typeList->size;
239}
240
241/*
242 * Compute the number of parameter words (u4 units) required by the
243 * given prototype. For example, if the method takes (int, long) and
244 * returns double, this would return 3 (one for the int, two for the
245 * long, and the return type isn't relevant).
246 */
247int dexProtoComputeArgsSize(const DexProto* pProto) {
248    const char* shorty = dexProtoGetShorty(pProto);
249    int count = 0;
250
251    /* Skip the return type. */
252    shorty++;
253
254    for (;;) {
255        switch (*(shorty++)) {
256            case '\0': {
257                return count;
258            }
259            case 'D':
260            case 'J': {
261                count += 2;
262                break;
263            }
264            default: {
265                count++;
266                break;
267            }
268        }
269    }
270}
271
272/*
273 * Common implementation for dexProtoCompare() and dexProtoCompareParameters().
274 */
275static int protoCompare(const DexProto* pProto1, const DexProto* pProto2,
276        bool compareReturnType) {
277
278    if (pProto1 == pProto2) {
279        // Easy out.
280        return 0;
281    } else {
282        const DexFile* dexFile1 = pProto1->dexFile;
283        const DexProtoId* protoId1 = getProtoId(pProto1);
284        const DexTypeList* typeList1 =
285            dexGetProtoParameters(dexFile1, protoId1);
286        int paramCount1 = (typeList1 == NULL) ? 0 : typeList1->size;
287
288        const DexFile* dexFile2 = pProto2->dexFile;
289        const DexProtoId* protoId2 = getProtoId(pProto2);
290        const DexTypeList* typeList2 =
291            dexGetProtoParameters(dexFile2, protoId2);
292        int paramCount2 = (typeList2 == NULL) ? 0 : typeList2->size;
293
294        if (protoId1 == protoId2) {
295            // Another easy out.
296            return 0;
297        }
298
299        // Compare return types.
300
301        if (compareReturnType) {
302            int result =
303                strcmp(dexStringByTypeIdx(dexFile1, protoId1->returnTypeIdx),
304                        dexStringByTypeIdx(dexFile2, protoId2->returnTypeIdx));
305
306            if (result != 0) {
307                return result;
308            }
309        }
310
311        // Compare parameters.
312
313        int minParam = (paramCount1 > paramCount2) ? paramCount2 : paramCount1;
314        int i;
315
316        for (i = 0; i < minParam; i++) {
317            u4 idx1 = dexTypeListGetIdx(typeList1, i);
318            u4 idx2 = dexTypeListGetIdx(typeList2, i);
319            int result =
320                strcmp(dexStringByTypeIdx(dexFile1, idx1),
321                        dexStringByTypeIdx(dexFile2, idx2));
322
323            if (result != 0) {
324                return result;
325            }
326        }
327
328        if (paramCount1 < paramCount2) {
329            return -1;
330        } else if (paramCount1 > paramCount2) {
331            return 1;
332        } else {
333            return 0;
334        }
335    }
336}
337
338/*
339 * Compare the two prototypes. The two prototypes are compared
340 * with the return type as the major order, then the first arguments,
341 * then second, etc. If two prototypes are identical except that one
342 * has extra arguments, then the shorter argument is considered the
343 * earlier one in sort order (similar to strcmp()).
344 */
345int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
346    return protoCompare(pProto1, pProto2, true);
347}
348
349/*
350 * Compare the two prototypes. The two prototypes are compared
351 * with the first argument as the major order, then second, etc. If two
352 * prototypes are identical except that one has extra arguments, then the
353 * shorter argument is considered the earlier one in sort order (similar
354 * to strcmp()).
355 */
356int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2){
357    return protoCompare(pProto1, pProto2, false);
358}
359
360
361/*
362 * Helper for dexProtoCompareToDescriptor(), which gets the return type
363 * descriptor from a method descriptor string.
364 */
365static const char* methodDescriptorReturnType(const char* descriptor) {
366    const char* result = strchr(descriptor, ')');
367
368    if (result == NULL) {
369        return NULL;
370    }
371
372    // The return type is the character just past the ')'.
373    return result + 1;
374}
375
376/*
377 * Helper for dexProtoCompareToDescriptor(), which indicates the end
378 * of an embedded argument type descriptor, which is also the
379 * beginning of the next argument type descriptor. Since this is for
380 * argument types, it doesn't accept 'V' as a valid type descriptor.
381 */
382static const char* methodDescriptorNextType(const char* descriptor) {
383    // Skip any array references.
384
385    while (*descriptor == '[') {
386        descriptor++;
387    }
388
389    switch (*descriptor) {
390        case 'B': case 'C': case 'D': case 'F':
391        case 'I': case 'J': case 'S': case 'Z': {
392            return descriptor + 1;
393        }
394        case 'L': {
395            const char* result = strchr(descriptor + 1, ';');
396            if (result != NULL) {
397                // The type ends just past the ';'.
398                return result + 1;
399            }
400        }
401    }
402
403    return NULL;
404}
405
406/*
407 * Compare a prototype and a string method descriptor. The comparison
408 * is done as if the descriptor were converted to a prototype and compared
409 * with dexProtoCompare().
410 */
411int dexProtoCompareToDescriptor(const DexProto* proto,
412        const char* descriptor) {
413    // First compare the return types.
414
415    int result = strcmp(dexProtoGetReturnType(proto),
416            methodDescriptorReturnType(descriptor));
417
418    if (result != 0) {
419        return result;
420    }
421
422    // The return types match, so we have to check arguments.
423
424    DexParameterIterator iterator;
425    dexParameterIteratorInit(&iterator, proto);
426
427    // Skip the '('.
428    assert (*descriptor == '(');
429    descriptor++;
430
431    for (;;) {
432        const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
433
434        if (*descriptor == ')') {
435            // It's the end of the descriptor string.
436            if (protoDesc == NULL) {
437                // It's also the end of the prototype's arguments.
438                return 0;
439            } else {
440                // The prototype still has more arguments.
441                return 1;
442            }
443        }
444
445        if (protoDesc == NULL) {
446            /*
447             * The prototype doesn't have arguments left, but the
448             * descriptor string does.
449             */
450            return -1;
451        }
452
453        // Both prototype and descriptor have arguments. Compare them.
454
455        const char* nextDesc = methodDescriptorNextType(descriptor);
456
457        for (;;) {
458            char c1 = *(protoDesc++);
459            char c2 = (descriptor < nextDesc) ? *(descriptor++) : '\0';
460
461            if (c1 < c2) {
462                // This includes the case where the proto is shorter.
463                return -1;
464            } else if (c1 > c2) {
465                // This includes the case where the desc is shorter.
466                return 1;
467            } else if (c1 == '\0') {
468                // The two types are equal in length. (c2 necessarily == '\0'.)
469                break;
470            }
471        }
472
473        /*
474         * If we made it here, the two arguments matched, and
475         * descriptor == nextDesc.
476         */
477    }
478}
479
480
481/*
482 * ===========================================================================
483 *      Parameter Iterators
484 * ===========================================================================
485 */
486
487/*
488 * Initialize the given DexParameterIterator to be at the start of the
489 * parameters of the given prototype.
490 */
491void dexParameterIteratorInit(DexParameterIterator* pIterator,
492        const DexProto* pProto) {
493    pIterator->proto = pProto;
494    pIterator->cursor = 0;
495
496    pIterator->parameters =
497        dexGetProtoParameters(pProto->dexFile, getProtoId(pProto));
498    pIterator->parameterCount = (pIterator->parameters == NULL) ? 0
499        : pIterator->parameters->size;
500}
501
502/*
503 * Get the type_id index for the next parameter, if any. This returns
504 * kDexNoIndex if the last parameter has already been consumed.
505 */
506u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator) {
507    int cursor = pIterator->cursor;
508    int parameterCount = pIterator->parameterCount;
509
510    if (cursor >= parameterCount) {
511        // The iteration is complete.
512        return kDexNoIndex;
513    } else {
514        u4 idx = dexTypeListGetIdx(pIterator->parameters, cursor);
515        pIterator->cursor++;
516        return idx;
517    }
518}
519
520/*
521 * Get the type descriptor for the next parameter, if any. This returns
522 * NULL if the last parameter has already been consumed.
523 */
524const char* dexParameterIteratorNextDescriptor(
525        DexParameterIterator* pIterator) {
526    u4 idx = dexParameterIteratorNextIndex(pIterator);
527
528    if (idx == kDexNoIndex) {
529        return NULL;
530    }
531
532    return dexStringByTypeIdx(pIterator->proto->dexFile, idx);
533}
534
535