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