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