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 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 */
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 /* (documented in header file) */
dexProtoGetShorty(const DexProto * pProto)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) */
dexProtoGetMethodDescriptor(const DexProto * pProto,DexStringCache * pCache)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) */
dexProtoCopyMethodDescriptor(const DexProto * pProto)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) */
dexProtoGetParameterDescriptors(const DexProto * pProto,DexStringCache * pCache)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) */
dexProtoGetReturnType(const DexProto * pProto)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) */
dexProtoGetParameterCount(const DexProto * pProto)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) */
dexProtoComputeArgsSize(const DexProto * pProto)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 */
protoCompare(const DexProto * pProto1,const DexProto * pProto2,bool compareReturnType)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) */
dexProtoCompare(const DexProto * pProto1,const DexProto * pProto2)318 int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
319 return protoCompare(pProto1, pProto2, true);
320 }
321
322 /* (documented in header file) */
dexProtoCompareParameters(const DexProto * pProto1,const DexProto * pProto2)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 */
methodDescriptorReturnType(const char * descriptor)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 */
methodDescriptorNextType(const char * descriptor)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 */
protoCompareToParameterDescriptors(const DexProto * proto,const char * descriptor,bool expectParens)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) */
dexProtoCompareToDescriptor(const DexProto * proto,const char * descriptor)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) */
dexProtoCompareToParameterDescriptors(const DexProto * proto,const char * descriptors)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 */
dexParameterIteratorInit(DexParameterIterator * pIterator,const DexProto * pProto)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 */
dexParameterIteratorNextIndex(DexParameterIterator * pIterator)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 */
dexParameterIteratorNextDescriptor(DexParameterIterator * pIterator)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