1/*
2 * Copyright 2008-2009, Axel D��rfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5
6
7#include <ctype.h>
8#include <stdlib.h>
9#include <string.h>
10
11#include <TypeConstants.h>
12
13#ifdef _KERNEL_MODE
14#	include <debug.h>
15#endif
16
17#include "demangle.h"
18
19
20//#define TRACE_GCC2_DEMANGLER
21#ifdef TRACE_GCC2_DEMANGLER
22#	include <stdio.h>
23#	define TRACE(x...) printf(x)
24#else
25#	define TRACE(x...) ;
26#endif
27
28
29class Input {
30public:
31	Input(const char* string, size_t length)
32		:
33		fString(string),
34		fLength(length)
35	{
36	}
37
38	const char* String() const
39	{
40		return fString;
41	}
42
43	int CharsRemaining() const
44	{
45		return fLength;
46	}
47
48	void Skip(size_t count)
49	{
50		if (count > fLength) {
51			TRACE("Input::Skip(): fOffset > fLength\n");
52			fString += fLength;
53			fLength = 0;
54			return;
55		}
56
57		fString += count;
58		fLength -= count;
59	}
60
61	Input& operator++()
62	{
63		Skip(1);
64		return *this;
65	}
66	Input operator++(int)
67	{
68		Input result(*this);
69		++(*this);
70		return result;
71	}
72
73	char operator[](size_t index) const
74	{
75		if (index >= fLength) {
76			TRACE("Input::operator[](): fOffset + index >= fLength\n");
77			return '\0';
78		}
79
80		return fString[index];
81	}
82
83private:
84	const char*	fString;
85	size_t		fLength;
86};
87
88
89enum {
90	TYPE_FUNCTION,
91	TYPE_METHOD,
92};
93
94
95static void
96ignore_qualifiers(Input& _arg)
97{
98	while (isupper(_arg[0])) {
99		if (_arg[0] == 'Q') {
100			// argument has namespaces
101			break;
102		}
103		if (_arg[0] == 'F') {
104			// skip function declaration
105			while (_arg.CharsRemaining() && _arg[0] != '_')
106				_arg++;
107
108			if (!_arg.CharsRemaining())
109				break;
110		}
111
112		_arg++;
113	}
114}
115
116
117static uint32
118argument_type(Input arg, size_t& length)
119{
120	length = sizeof(int);
121
122	switch (char type = arg[0]) {
123		case 'P':	// pointer
124		case 'R':	// reference
125			length = sizeof(void*);
126			ignore_qualifiers(arg);
127			if (arg[0] == 'c')
128				return B_STRING_TYPE;
129			if (arg[0] == 't') {
130				// TODO: templates are not yet supported
131				return 0;
132			}
133
134			return type == 'P' ? B_POINTER_TYPE : B_REF_TYPE;
135		case 'x':
136			length = sizeof(long long);
137			return B_INT64_TYPE;
138		case 'l':
139			if (sizeof(long) == 4)
140				return B_INT32_TYPE;
141			return B_INT64_TYPE;
142		case 'i':
143			return B_INT32_TYPE;
144		case 's':
145			return B_INT16_TYPE;
146		case 'c':
147			return B_INT8_TYPE;
148		case 'b':
149			return B_BOOL_TYPE;
150		case 'U':
151			switch (arg[1]) {
152				case 'x':
153					length = sizeof(long long);
154					return B_UINT64_TYPE;
155				case 'l':
156					if (sizeof(long) == 4)
157						return B_UINT32_TYPE;
158					return B_UINT64_TYPE;
159				case 'i':
160					return B_UINT32_TYPE;
161				case 's':
162					return B_UINT16_TYPE;
163				case 'c':
164					return B_UINT8_TYPE;
165				default:
166					return B_UINT32_TYPE;
167			}
168			break;
169
170		case 'f':
171			return B_FLOAT_TYPE;
172		case 'd':
173			length = sizeof(double);
174			return B_DOUBLE_TYPE;
175		case 'r':
176			// TODO: is "long double" supported under Haiku at all?
177			return 0;
178
179		case 't':
180			// TODO: templates are not yet supported
181			return 0;
182
183		default:
184			return B_ANY_TYPE;
185	}
186}
187
188
189static uint32
190parse_number(Input& _arg, bool numberLeft)
191{
192	Input arg = _arg;
193
194	while (isdigit(arg[0]))
195		arg++;
196
197	uint32 value;
198
199	if (arg[0] == '_' && (!numberLeft || isdigit(arg[1]))) {
200		// long value
201		const char* tmp_arg = _arg.String();
202		value = strtoul(tmp_arg, (char**)&tmp_arg, 10);
203		_arg.Skip(tmp_arg - _arg.String());
204		if (_arg[0] == '_')
205			_arg++;
206	} else {
207		value = _arg[0] - '0';
208		_arg++;
209	}
210
211	return value;
212}
213
214
215static uint32
216parse_repeats(Input& _arg, uint32& index)
217{
218	if (_arg[0] != 'N')
219		return 0;
220
221	_arg++;
222
223	uint32 count = parse_number(_arg, true);
224	index = parse_number(_arg, false);
225
226	return count;
227}
228
229
230static void
231skip_numbers(Input& _arg, int32 count)
232{
233	// skip leading character
234	_arg++;
235
236	while (count-- > 0) {
237		parse_number(_arg, count != 0);
238	}
239}
240
241
242static uint32
243count_namespaces(Input& _mangled)
244{
245	Input mangled = _mangled;
246
247	int32 namespaces = 0;
248	if (mangled[0] == 'Q') {
249		// more than one namespace
250		if (mangled[1] == '_') {
251			// more than 9 namespaces
252			const char* mangled_str = mangled.String();
253			namespaces = strtoul(mangled_str + 2, (char**)&mangled_str, 10);
254			mangled.Skip(mangled_str - mangled.String());
255			if (mangled[0] != '_')
256				namespaces = 0;
257
258			mangled++;
259		} else {
260			namespaces = mangled[1] - '0';
261			mangled.Skip(2);
262		}
263	} else if (isdigit(mangled[0]))
264		namespaces = 1;
265
266	_mangled = mangled;
267	return namespaces;
268}
269
270
271static void
272skip_namespaces(Input& mangled)
273{
274	int32 namespaces = count_namespaces(mangled);
275
276	while (namespaces-- > 0) {
277		if (!isdigit(mangled[0]))
278			break;
279
280		const char* mangled_str = mangled.String();
281		mangled_str += strtoul(mangled_str, (char**)&mangled_str, 10);
282		mangled.Skip(mangled_str - mangled.String());
283	}
284}
285
286
287static bool
288has_named_argument(Input& arg)
289{
290	ignore_qualifiers(arg);
291
292	// See if it's a built-in type
293	return arg[0] == 'Q' || isdigit(arg[0]);
294}
295
296
297static uint32
298argument_length(Input& _arg)
299{
300	if (_arg[0] == 'N') {
301		// skip repeats
302		skip_numbers(_arg, 2);
303		return 0;
304	} else if (_arg[0] == 'T') {
305		// skip reference
306		skip_numbers(_arg, 1);
307		return 0;
308	}
309
310	ignore_qualifiers(_arg);
311
312	if (!_arg.CharsRemaining())
313		return 0;
314
315	// See if it's a built-in type
316	if (_arg[0] != 'Q' && !isdigit(_arg[0]))
317		return 1;
318
319	Input mangled = _arg;
320	skip_namespaces(mangled);
321
322	return mangled.String() - _arg.String();
323}
324
325
326static Input
327next_argument(Input arg)
328{
329	if (!arg.CharsRemaining() || !arg[0])
330		return arg;
331
332	uint32 length = argument_length(arg);
333	arg.Skip(length);
334
335	return arg;
336}
337
338
339static Input
340first_argument(Input mangled)
341{
342	skip_namespaces(mangled);
343
344	return mangled;
345}
346
347
348static bool
349mangled_start(Input& name, size_t* _symbolLength, int32* _symbolType)
350{
351	// search '__' starting from the end, don't accept them at the start
352	size_t pos = name.CharsRemaining() - 1;
353	bool foundStart = false;
354
355	while (pos > 1) {
356		if (name[pos] == '_') {
357			if (name[pos - 1] == '_') {
358				foundStart = true;
359				name.Skip(pos + 1);
360				break;
361			} else
362				pos--;
363		}
364		pos--;
365	}
366
367	if (!foundStart)
368		return false;
369
370	if (name[0] == 'H') {
371		// TODO: we don't support templates yet
372		return false;
373	}
374
375	if (_symbolLength != NULL)
376		*_symbolLength = pos - 1;
377
378	if (name[0] == 'F') {
379		if (_symbolType != NULL)
380			*_symbolType = TYPE_FUNCTION;
381		name.Skip(1);
382		return true;
383	}
384
385	if (_symbolType != NULL)
386		*_symbolType = TYPE_METHOD;
387	return true;
388}
389
390
391static status_t
392get_next_argument_internal(uint32* _cookie, const char* symbol, char* name,
393	size_t nameSize, int32* _type, size_t* _argumentLength, bool repeating)
394{
395	Input mangled(symbol, strlen(symbol));
396	if (!mangled_start(mangled, NULL, NULL))
397		return B_BAD_VALUE;
398
399	Input arg = first_argument(mangled);
400
401	// (void) is not an argument
402	if (arg[0] == 'v')
403		return B_ENTRY_NOT_FOUND;
404
405	uint32 current = *_cookie;
406	if (current > 32)
407		return B_TOO_MANY_ARGS;
408
409	for (uint32 i = 0; i < current; i++) {
410		arg = next_argument(arg);
411		if (arg.CharsRemaining() && arg[0] == 'N') {
412			// repeat argument 'count' times
413			uint32 index;
414			uint32 count = parse_repeats(arg, index);
415			if (current <= i + count) {
416				if (repeating)
417					return B_LINK_LIMIT;
418
419				// it's a repeat case
420				status_t status = get_next_argument_internal(&index, symbol,
421					name, nameSize, _type, _argumentLength, true);
422				if (status == B_OK)
423					(*_cookie)++;
424				return status;
425			}
426
427			i += count - 1;
428		}
429	}
430
431	if (arg[0] == '\0' || !arg.CharsRemaining())
432		return B_ENTRY_NOT_FOUND;
433
434	TRACE("\n\targ %ld: %s\n", current, arg.String());
435
436	if (arg[0] == 'T') {
437		// duplicate argument
438		if (repeating)
439			return B_LINK_LIMIT;
440
441		arg++;
442		uint32 index = parse_number(arg, false);
443		status_t status = get_next_argument_internal(&index, symbol, name,
444			nameSize, _type, _argumentLength, true);
445		if (status == B_OK)
446			(*_cookie)++;
447		return status;
448	}
449
450	(*_cookie)++;
451
452	size_t argumentLength;
453	int32 type = argument_type(arg, argumentLength);
454	if (type == 0)
455		return B_NOT_SUPPORTED;
456
457	if (_type != NULL)
458		*_type = type;
459	if (_argumentLength != NULL)
460		*_argumentLength = argumentLength;
461
462	name[0] = '\0';
463	if (!has_named_argument(arg))
464		return B_OK;
465
466	Input namespaceStart = arg;
467	int32 namespaces = count_namespaces(namespaceStart);
468
469	while (namespaces-- > 0) {
470		if (namespaceStart[0] == 't') {
471			// It's a template class after all
472			return B_BAD_TYPE;
473		}
474		if (!isdigit(namespaceStart[0]))
475			break;
476
477		const char* ns_str = namespaceStart.String();
478		uint32 length = strtoul(ns_str, (char**)&ns_str, 10);
479		namespaceStart.Skip(ns_str - namespaceStart.String());
480
481		uint32 max = strlen(name) + length + 1;
482		strlcat(name, namespaceStart.String(), min_c(max, nameSize));
483		if (namespaces > 0)
484			strlcat(name, "::", nameSize);
485		namespaceStart.Skip(length);
486	}
487
488	return B_OK;
489}
490
491
492//	#pragma mark -
493
494
495const char*
496demangle_symbol_gcc2(const char* name, char* buffer, size_t bufferSize,
497	bool* _isObjectMethod)
498{
499	if (name == NULL)
500		return NULL;
501
502	size_t nameLength;
503	int32 type;
504	Input mangled(name, strlen(name));
505	if (!mangled_start(mangled, &nameLength, &type))
506		return NULL;
507
508	if (mangled[0] == 'C') {
509		// ignore const method
510		type = TYPE_METHOD;
511		mangled++;
512	}
513
514	if (_isObjectMethod != NULL) {
515		// we can only guess with GCC2 mangling
516		*_isObjectMethod = type == TYPE_METHOD;
517	}
518
519	Input namespaceStart = mangled;
520	int32 namespaces = count_namespaces(namespaceStart);
521
522	buffer[0] = '\0';
523
524	while (namespaces-- > 0) {
525		if (namespaceStart[0] == 't') {
526			// It's a template class after all
527			return NULL;
528		}
529		if (!isdigit(namespaceStart[0]))
530			break;
531
532		const char* ns_str = namespaceStart.String();
533		uint32 length = strtoul(ns_str, (char**)&ns_str, 10);
534		namespaceStart.Skip(ns_str - namespaceStart.String());
535
536		uint32 max = strlen(buffer) + length + 1;
537		strlcat(buffer, namespaceStart.String(), min_c(max, bufferSize));
538		strlcat(buffer, "::", bufferSize);
539		namespaceStart.Skip(length);
540	}
541
542	size_t max = strlen(buffer) + nameLength + 1;
543	strlcat(buffer, name, min_c(max, bufferSize));
544	return buffer;
545}
546
547
548status_t
549get_next_argument_gcc2(uint32* _cookie, const char* symbol, char* name,
550	size_t nameSize, int32* _type, size_t* _argumentLength)
551{
552	status_t error = get_next_argument_internal(_cookie, symbol, name, nameSize,
553		_type, _argumentLength, false);
554	if (error != B_OK)
555		return error;
556
557	// append the missing '*'/'&' for pointer/ref types
558	if (name[0] != '\0' && (*_type == B_POINTER_TYPE || *_type == B_REF_TYPE))
559		strlcat(name, *_type == B_POINTER_TYPE ? "*" : "&", nameSize);
560
561	return B_OK;
562}
563