OSDN Git Service

TypeRef, not ClassRef.
[android-x86/dalvik.git] / libdex / DexProto.c
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 }