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 static 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  */
     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 /*
    126  * Get the short-form method descriptor for the given prototype. The
    127  * prototype must be protoIdx-based.
    128  */
    129 const 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  */
    138 const 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  */
    176 char* 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  */
    189 const 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  */
    226 const 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  */
    234 size_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  */
    247 int 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  */
    275 static 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  */
    345 int 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  */
    356 int 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  */
    365 static 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  */
    382 static 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  */
    411 int 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  */
    491 void 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  */
    506 u4 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  */
    524 const 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