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