1/*
2 * Copyright (c) 1999-2007 Apple Inc.  All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/***********************************************************************
25* objc-typeencoding.m
26* Parsing of old-style type strings.
27**********************************************************************/
28
29#include "objc-private.h"
30
31/***********************************************************************
32* SubtypeUntil.
33*
34* Delegation.
35**********************************************************************/
36static int	SubtypeUntil	       (const char *	type,
37                                char		end)
38{
39    int		level = 0;
40    const char *	head = type;
41
42    //
43    while (*type)
44    {
45        if (!*type || (!level && (*type == end)))
46            return (int)(type - head);
47
48        switch (*type)
49        {
50            case ']': case '}': case ')': level--; break;
51            case '[': case '{': case '(': level += 1; break;
52        }
53
54        type += 1;
55    }
56
57    _objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n");
58    return 0;
59}
60
61
62/***********************************************************************
63* SkipFirstType.
64**********************************************************************/
65static const char *	SkipFirstType	   (const char *	type)
66{
67    while (1)
68    {
69        switch (*type++)
70        {
71            case 'O':	/* bycopy */
72            case 'n':	/* in */
73            case 'o':	/* out */
74            case 'N':	/* inout */
75            case 'r':	/* const */
76            case 'V':	/* oneway */
77            case '^':	/* pointers */
78                break;
79
80            case '@':   /* objects */
81                if (type[0] == '?') type++;  /* Blocks */
82                return type;
83
84                /* arrays */
85            case '[':
86                while ((*type >= '0') && (*type <= '9'))
87                    type += 1;
88                return type + SubtypeUntil (type, ']') + 1;
89
90                /* structures */
91            case '{':
92                return type + SubtypeUntil (type, '}') + 1;
93
94                /* unions */
95            case '(':
96                return type + SubtypeUntil (type, ')') + 1;
97
98                /* basic types */
99            default:
100                return type;
101        }
102    }
103}
104
105
106/***********************************************************************
107* encoding_getNumberOfArguments.
108**********************************************************************/
109unsigned int
110encoding_getNumberOfArguments(const char *typedesc)
111{
112    unsigned nargs;
113
114    // First, skip the return type
115    typedesc = SkipFirstType (typedesc);
116
117    // Next, skip stack size
118    while ((*typedesc >= '0') && (*typedesc <= '9'))
119        typedesc += 1;
120
121    // Now, we have the arguments - count how many
122    nargs = 0;
123    while (*typedesc)
124    {
125        // Traverse argument type
126        typedesc = SkipFirstType (typedesc);
127
128        // Skip GNU runtime's register parameter hint
129        if (*typedesc == '+') typedesc++;
130
131        // Traverse (possibly negative) argument offset
132        if (*typedesc == '-')
133            typedesc += 1;
134        while ((*typedesc >= '0') && (*typedesc <= '9'))
135            typedesc += 1;
136
137        // Made it past an argument
138        nargs += 1;
139    }
140
141    return nargs;
142}
143
144/***********************************************************************
145* encoding_getSizeOfArguments.
146**********************************************************************/
147unsigned
148encoding_getSizeOfArguments(const char *typedesc)
149{
150    unsigned		stack_size;
151
152    // Get our starting points
153    stack_size = 0;
154
155    // Skip the return type
156    typedesc = SkipFirstType (typedesc);
157
158    // Convert ASCII number string to integer
159    while ((*typedesc >= '0') && (*typedesc <= '9'))
160        stack_size = (stack_size * 10) + (*typedesc++ - '0');
161
162    return stack_size;
163}
164
165
166/***********************************************************************
167* encoding_getArgumentInfo.
168**********************************************************************/
169unsigned int
170encoding_getArgumentInfo(const char *typedesc, unsigned int arg,
171                         const char **type, int *offset)
172{
173    unsigned nargs = 0;
174    int self_offset = 0;
175    BOOL offset_is_negative = NO;
176
177    // First, skip the return type
178    typedesc = SkipFirstType (typedesc);
179
180    // Next, skip stack size
181    while ((*typedesc >= '0') && (*typedesc <= '9'))
182        typedesc += 1;
183
184    // Now, we have the arguments - position typedesc to the appropriate argument
185    while (*typedesc && nargs != arg)
186    {
187
188        // Skip argument type
189        typedesc = SkipFirstType (typedesc);
190
191        if (nargs == 0)
192        {
193            // Skip GNU runtime's register parameter hint
194            if (*typedesc == '+') typedesc++;
195
196            // Skip negative sign in offset
197            if (*typedesc == '-')
198            {
199                offset_is_negative = YES;
200                typedesc += 1;
201            }
202            else
203                offset_is_negative = NO;
204
205            while ((*typedesc >= '0') && (*typedesc <= '9'))
206                self_offset = self_offset * 10 + (*typedesc++ - '0');
207            if (offset_is_negative)
208                self_offset = -(self_offset);
209
210        }
211
212        else
213        {
214            // Skip GNU runtime's register parameter hint
215            if (*typedesc == '+') typedesc++;
216
217            // Skip (possibly negative) argument offset
218            if (*typedesc == '-')
219                typedesc += 1;
220            while ((*typedesc >= '0') && (*typedesc <= '9'))
221                typedesc += 1;
222        }
223
224        nargs += 1;
225    }
226
227    if (*typedesc)
228    {
229        int arg_offset = 0;
230
231        *type	 = typedesc;
232        typedesc = SkipFirstType (typedesc);
233
234        if (arg == 0)
235        {
236            *offset = 0;
237        }
238
239        else
240        {
241            // Skip GNU register parameter hint
242            if (*typedesc == '+') typedesc++;
243
244            // Pick up (possibly negative) argument offset
245            if (*typedesc == '-')
246            {
247                offset_is_negative = YES;
248                typedesc += 1;
249            }
250            else
251                offset_is_negative = NO;
252
253            while ((*typedesc >= '0') && (*typedesc <= '9'))
254                arg_offset = arg_offset * 10 + (*typedesc++ - '0');
255            if (offset_is_negative)
256                arg_offset = - arg_offset;
257
258            *offset = arg_offset - self_offset;
259        }
260
261    }
262
263    else
264    {
265        *type	= 0;
266        *offset	= 0;
267    }
268
269    return nargs;
270}
271
272
273void
274encoding_getReturnType(const char *t, char *dst, size_t dst_len)
275{
276    size_t len;
277    const char *end;
278
279    if (!dst) return;
280    if (!t) {
281        strncpy(dst, "", dst_len);
282        return;
283    }
284
285    end = SkipFirstType(t);
286    len = end - t;
287    strncpy(dst, t, MIN(len, dst_len));
288    if (len < dst_len) memset(dst+len, 0, dst_len - len);
289}
290
291/***********************************************************************
292* encoding_copyReturnType.  Returns the method's return type string
293* on the heap.
294**********************************************************************/
295char *
296encoding_copyReturnType(const char *t)
297{
298    size_t len;
299    const char *end;
300    char *result;
301
302    if (!t) return NULL;
303
304    end = SkipFirstType(t);
305    len = end - t;
306    result = (char *)malloc(len + 1);
307    strncpy(result, t, len);
308    result[len] = '\0';
309    return result;
310}
311
312
313void
314encoding_getArgumentType(const char *t, unsigned int index,
315                         char *dst, size_t dst_len)
316{
317    size_t len;
318    const char *end;
319    int offset;
320
321    if (!dst) return;
322    if (!t) {
323        strncpy(dst, "", dst_len);
324        return;
325    }
326
327    encoding_getArgumentInfo(t, index, &t, &offset);
328
329    if (!t) {
330        strncpy(dst, "", dst_len);
331        return;
332    }
333
334    end = SkipFirstType(t);
335    len = end - t;
336    strncpy(dst, t, MIN(len, dst_len));
337    if (len < dst_len) memset(dst+len, 0, dst_len - len);
338}
339
340
341/***********************************************************************
342* encoding_copyArgumentType.  Returns a single argument's type string
343* on the heap. Argument 0 is `self`; argument 1 is `_cmd`.
344**********************************************************************/
345char *
346encoding_copyArgumentType(const char *t, unsigned int index)
347{
348    size_t len;
349    const char *end;
350    char *result;
351    int offset;
352
353    if (!t) return NULL;
354
355    encoding_getArgumentInfo(t, index, &t, &offset);
356
357    if (!t) return NULL;
358
359    end = SkipFirstType(t);
360    len = end - t;
361    result = (char *)malloc(len + 1);
362    strncpy(result, t, len);
363    result[len] = '\0';
364    return result;
365}
366