1// SPDX-License-Identifier: GPL-2.0
2#include <sys/types.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include "symbol.h"
7
8#include "demangle-java.h"
9
10#include <linux/ctype.h>
11#include <linux/kernel.h>
12
13enum {
14	MODE_PREFIX = 0,
15	MODE_CLASS  = 1,
16	MODE_FUNC   = 2,
17	MODE_TYPE   = 3,
18	MODE_CTYPE  = 4, /* class arg */
19};
20
21#define BASE_ENT(c, n)	[c - 'A']=n
22static 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', "boolean" ),
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 */
44static 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_TYPE) {
63				if (mode == MODE_TYPE) {
64					if (narg)
65						rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
66					narg++;
67				}
68				if (mode == MODE_PREFIX)
69					mode = MODE_CLASS;
70				else
71					mode = MODE_CTYPE;
72			} else
73				buf[rlen++] = *q;
74			break;
75		case 'B':
76		case 'C':
77		case 'D':
78		case 'F':
79		case 'I':
80		case 'J':
81		case 'S':
82		case 'Z':
83			if (mode == MODE_TYPE) {
84				if (narg)
85					rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
86				rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']);
87				while (array--)
88					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
89				array = 0;
90				narg++;
91			} else
92				buf[rlen++] = *q;
93			break;
94		case 'V':
95			if (mode == MODE_TYPE) {
96				rlen += scnprintf(buf + rlen, maxlen - rlen, "void");
97				while (array--)
98					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
99				array = 0;
100			} else
101				buf[rlen++] = *q;
102			break;
103		case '[':
104			if (mode != MODE_TYPE)
105				goto error;
106			array++;
107			break;
108		case '(':
109			if (mode != MODE_FUNC)
110				goto error;
111			buf[rlen++] = *q;
112			mode = MODE_TYPE;
113			break;
114		case ')':
115			if (mode != MODE_TYPE)
116				goto error;
117			buf[rlen++] = *q;
118			narg = 0;
119			break;
120		case ';':
121			if (mode != MODE_CLASS && mode != MODE_CTYPE)
122				goto error;
123			/* safe because at least one other char to process */
124			if (isalpha(*(q + 1)) && mode == MODE_CLASS)
125				rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
126			if (mode == MODE_CLASS)
127				mode = MODE_FUNC;
128			else if (mode == MODE_CTYPE)
129				mode = MODE_TYPE;
130			break;
131		case '/':
132			if (mode != MODE_CLASS && mode != MODE_CTYPE)
133				goto error;
134			rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
135			break;
136		default :
137			buf[rlen++] = *q;
138		}
139	}
140	buf[rlen] = '\0';
141	return buf;
142error:
143	return NULL;
144}
145
146/*
147 * Demangle Java function signature (openJDK, not GCJ)
148 * input:
149 * 	str: string to parse. String is not modified
150 *    flags: combination of JAVA_DEMANGLE_* flags to modify demangling
151 * return:
152 *	if input can be demangled, then a newly allocated string is returned.
153 *	if input cannot be demangled, then NULL is returned
154 *
155 * Note: caller is responsible for freeing demangled string
156 */
157char *
158java_demangle_sym(const char *str, int flags)
159{
160	char *buf, *ptr;
161	char *p;
162	size_t len, l1 = 0;
163
164	if (!str)
165		return NULL;
166
167	/* find start of return type */
168	p = strrchr(str, ')');
169	if (!p)
170		return NULL;
171
172	/*
173	 * expansion factor estimated to 3x
174	 */
175	len = strlen(str) * 3 + 1;
176	buf = malloc(len);
177	if (!buf)
178		return NULL;
179
180	buf[0] = '\0';
181	if (!(flags & JAVA_DEMANGLE_NORET)) {
182		/*
183		 * get return type first
184		 */
185		ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE);
186		if (!ptr)
187			goto error;
188
189		/* add space between return type and function prototype */
190		l1 = strlen(buf);
191		buf[l1++] = ' ';
192	}
193
194	/* process function up to return type */
195	ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX);
196	if (!ptr)
197		goto error;
198
199	return buf;
200error:
201	free(buf);
202	return NULL;
203}
204