xref: /linux/tools/perf/util/demangle-java.c (revision 80d443e8876602be2c130f79c4de81e12e2a700d)
1 #include <sys/types.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include "util.h"
5 #include "debug.h"
6 #include "symbol.h"
7 
8 #include "demangle-java.h"
9 
10 enum {
11 	MODE_PREFIX = 0,
12 	MODE_CLASS  = 1,
13 	MODE_FUNC   = 2,
14 	MODE_TYPE   = 3,
15 	MODE_CTYPE  = 3, /* class arg */
16 };
17 
18 #define BASE_ENT(c, n)	[c - 'A']=n
19 static const char *base_types['Z' - 'A' + 1] = {
20 	BASE_ENT('B', "byte" ),
21 	BASE_ENT('C', "char" ),
22 	BASE_ENT('D', "double" ),
23 	BASE_ENT('F', "float" ),
24 	BASE_ENT('I', "int" ),
25 	BASE_ENT('J', "long" ),
26 	BASE_ENT('S', "short" ),
27 	BASE_ENT('Z', "bool" ),
28 };
29 
30 /*
31  * demangle Java symbol between str and end positions and stores
32  * up to maxlen characters into buf. The parser starts in mode.
33  *
34  * Use MODE_PREFIX to process entire prototype till end position
35  * Use MODE_TYPE to process return type if str starts on return type char
36  *
37  *  Return:
38  *	success: buf
39  *	error  : NULL
40  */
41 static char *
42 __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode)
43 {
44 	int rlen = 0;
45 	int array = 0;
46 	int narg = 0;
47 	const char *q;
48 
49 	if (!end)
50 		end = str + strlen(str);
51 
52 	for (q = str; q != end; q++) {
53 
54 		if (rlen == (maxlen - 1))
55 			break;
56 
57 		switch (*q) {
58 		case 'L':
59 			if (mode == MODE_PREFIX || mode == MODE_CTYPE) {
60 				if (mode == MODE_CTYPE) {
61 					if (narg)
62 						rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
63 					narg++;
64 				}
65 				rlen += scnprintf(buf + rlen, maxlen - rlen, "class ");
66 				if (mode == MODE_PREFIX)
67 					mode = MODE_CLASS;
68 			} else
69 				buf[rlen++] = *q;
70 			break;
71 		case 'B':
72 		case 'C':
73 		case 'D':
74 		case 'F':
75 		case 'I':
76 		case 'J':
77 		case 'S':
78 		case 'Z':
79 			if (mode == MODE_TYPE) {
80 				if (narg)
81 					rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
82 				rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']);
83 				while (array--)
84 					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
85 				array = 0;
86 				narg++;
87 			} else
88 				buf[rlen++] = *q;
89 			break;
90 		case 'V':
91 			if (mode == MODE_TYPE) {
92 				rlen += scnprintf(buf + rlen, maxlen - rlen, "void");
93 				while (array--)
94 					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
95 				array = 0;
96 			} else
97 				buf[rlen++] = *q;
98 			break;
99 		case '[':
100 			if (mode != MODE_TYPE)
101 				goto error;
102 			array++;
103 			break;
104 		case '(':
105 			if (mode != MODE_FUNC)
106 				goto error;
107 			buf[rlen++] = *q;
108 			mode = MODE_TYPE;
109 			break;
110 		case ')':
111 			if (mode != MODE_TYPE)
112 				goto error;
113 			buf[rlen++] = *q;
114 			narg = 0;
115 			break;
116 		case ';':
117 			if (mode != MODE_CLASS && mode != MODE_CTYPE)
118 				goto error;
119 			/* safe because at least one other char to process */
120 			if (isalpha(*(q + 1)))
121 				rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
122 			if (mode == MODE_CLASS)
123 				mode = MODE_FUNC;
124 			else if (mode == MODE_CTYPE)
125 				mode = MODE_TYPE;
126 			break;
127 		case '/':
128 			if (mode != MODE_CLASS && mode != MODE_CTYPE)
129 				goto error;
130 			rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
131 			break;
132 		default :
133 			buf[rlen++] = *q;
134 		}
135 	}
136 	buf[rlen] = '\0';
137 	return buf;
138 error:
139 	return NULL;
140 }
141 
142 /*
143  * Demangle Java function signature (openJDK, not GCJ)
144  * input:
145  * 	str: string to parse. String is not modified
146  *    flags: comobination of JAVA_DEMANGLE_* flags to modify demangling
147  * return:
148  *	if input can be demangled, then a newly allocated string is returned.
149  *	if input cannot be demangled, then NULL is returned
150  *
151  * Note: caller is responsible for freeing demangled string
152  */
153 char *
154 java_demangle_sym(const char *str, int flags)
155 {
156 	char *buf, *ptr;
157 	char *p;
158 	size_t len, l1 = 0;
159 
160 	if (!str)
161 		return NULL;
162 
163 	/* find start of retunr type */
164 	p = strrchr(str, ')');
165 	if (!p)
166 		return NULL;
167 
168 	/*
169 	 * expansion factor estimated to 3x
170 	 */
171 	len = strlen(str) * 3 + 1;
172 	buf = malloc(len);
173 	if (!buf)
174 		return NULL;
175 
176 	buf[0] = '\0';
177 	if (!(flags & JAVA_DEMANGLE_NORET)) {
178 		/*
179 		 * get return type first
180 		 */
181 		ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE);
182 		if (!ptr)
183 			goto error;
184 
185 		/* add space between return type and function prototype */
186 		l1 = strlen(buf);
187 		buf[l1++] = ' ';
188 	}
189 
190 	/* process function up to return type */
191 	ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX);
192 	if (!ptr)
193 		goto error;
194 
195 	return buf;
196 error:
197 	free(buf);
198 	return NULL;
199 }
200