Home | History | Annotate | Download | only in libdex
      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  */
     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  */
     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  */
     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  */
     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  */
     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  */
    121 static inline const DexProtoId* getProtoId(const DexProto* pProto) {
    122     return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
    123 }
    124 
    125 /* (documented in header file) */
    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) */
    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) */
    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) */
    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) */
    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) */
    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) */
    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  */
    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) */
    318 int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
    319     return protoCompare(pProto1, pProto2, true);
    320 }
    321 
    322 /* (documented in header file) */
    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  */
    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  */
    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  */
    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) */
    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) */
    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  */
    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  */
    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  */
    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