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 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 */
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 /*
126 * Get the short-form method descriptor for the given prototype. The
127 * prototype must be protoIdx-based.
128 */
dexProtoGetShorty(const DexProto * pProto)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 */
dexProtoGetMethodDescriptor(const DexProto * pProto,DexStringCache * pCache)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 */
dexProtoCopyMethodDescriptor(const DexProto * pProto)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 */
dexProtoGetParameterDescriptors(const DexProto * pProto,DexStringCache * pCache)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 */
dexProtoGetReturnType(const DexProto * pProto)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 */
dexProtoGetParameterCount(const DexProto * pProto)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 */
dexProtoComputeArgsSize(const DexProto * pProto)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 */
protoCompare(const DexProto * pProto1,const DexProto * pProto2,bool compareReturnType)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 */
dexProtoCompare(const DexProto * pProto1,const DexProto * pProto2)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 */
dexProtoCompareParameters(const DexProto * pProto1,const DexProto * pProto2)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 */
methodDescriptorReturnType(const char * descriptor)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 */
methodDescriptorNextType(const char * descriptor)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 */
dexProtoCompareToDescriptor(const DexProto * proto,const char * descriptor)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 */
dexParameterIteratorInit(DexParameterIterator * pIterator,const DexProto * pProto)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 */
dexParameterIteratorNextIndex(DexParameterIterator * pIterator)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 */
dexParameterIteratorNextDescriptor(DexParameterIterator * pIterator)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