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  */
dexStringCacheAlloc(DexStringCache * pCache,size_t length)36 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 = (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  */
dexStringCacheInit(DexStringCache * pCache)57 void 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  */
dexStringCacheRelease(DexStringCache * pCache)67 void 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  */
dexStringCacheEnsureCopy(DexStringCache * pCache,const char * value)82 char* 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  */
dexStringCacheAbandon(DexStringCache * pCache,const char * value)99 char* 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  */
getProtoId(const DexProto * pProto)121 static inline const DexProtoId* getProtoId(const DexProto* pProto) {
122     return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
123 }
124 
125 /* (documented in header file) */
dexProtoGetShorty(const DexProto * pProto)126 const 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) */
dexProtoGetMethodDescriptor(const DexProto * pProto,DexStringCache * pCache)133 const 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) */
dexProtoCopyMethodDescriptor(const DexProto * pProto)168 char* 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) */
dexProtoGetParameterDescriptors(const DexProto * pProto,DexStringCache * pCache)177 const 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) */
dexProtoGetReturnType(const DexProto * pProto)212 const 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) */
dexProtoGetParameterCount(const DexProto * pProto)218 size_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) */
dexProtoComputeArgsSize(const DexProto * pProto)226 int 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  */
protoCompare(const DexProto * pProto1,const DexProto * pProto2,bool compareReturnType)254 static 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) */
dexProtoCompare(const DexProto * pProto1,const DexProto * pProto2)318 int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
319     return protoCompare(pProto1, pProto2, true);
320 }
321 
322 /* (documented in header file) */
dexProtoCompareParameters(const DexProto * pProto1,const DexProto * pProto2)323 int 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  */
methodDescriptorReturnType(const char * descriptor)332 static 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  */
methodDescriptorNextType(const char * descriptor)349 static 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  */
protoCompareToParameterDescriptors(const DexProto * proto,const char * descriptor,bool expectParens)380 static 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) */
dexProtoCompareToDescriptor(const DexProto * proto,const char * descriptor)443 int 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) */
dexProtoCompareToParameterDescriptors(const DexProto * proto,const char * descriptors)461 int 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  */
dexParameterIteratorInit(DexParameterIterator * pIterator,const DexProto * pProto)481 void 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  */
dexParameterIteratorNextIndex(DexParameterIterator * pIterator)496 u4 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  */
dexParameterIteratorNextDescriptor(DexParameterIterator * pIterator)514 const 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