162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <sys/types.h>
362306a36Sopenharmony_ci#include <stdio.h>
462306a36Sopenharmony_ci#include <stdlib.h>
562306a36Sopenharmony_ci#include <string.h>
662306a36Sopenharmony_ci#include "symbol.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "demangle-java.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/ctype.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cienum {
1462306a36Sopenharmony_ci	MODE_PREFIX = 0,
1562306a36Sopenharmony_ci	MODE_CLASS  = 1,
1662306a36Sopenharmony_ci	MODE_FUNC   = 2,
1762306a36Sopenharmony_ci	MODE_TYPE   = 3,
1862306a36Sopenharmony_ci	MODE_CTYPE  = 4, /* class arg */
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define BASE_ENT(c, n)	[c - 'A']=n
2262306a36Sopenharmony_cistatic const char *base_types['Z' - 'A' + 1] = {
2362306a36Sopenharmony_ci	BASE_ENT('B', "byte" ),
2462306a36Sopenharmony_ci	BASE_ENT('C', "char" ),
2562306a36Sopenharmony_ci	BASE_ENT('D', "double" ),
2662306a36Sopenharmony_ci	BASE_ENT('F', "float" ),
2762306a36Sopenharmony_ci	BASE_ENT('I', "int" ),
2862306a36Sopenharmony_ci	BASE_ENT('J', "long" ),
2962306a36Sopenharmony_ci	BASE_ENT('S', "short" ),
3062306a36Sopenharmony_ci	BASE_ENT('Z', "boolean" ),
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * demangle Java symbol between str and end positions and stores
3562306a36Sopenharmony_ci * up to maxlen characters into buf. The parser starts in mode.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Use MODE_PREFIX to process entire prototype till end position
3862306a36Sopenharmony_ci * Use MODE_TYPE to process return type if str starts on return type char
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci *  Return:
4162306a36Sopenharmony_ci *	success: buf
4262306a36Sopenharmony_ci *	error  : NULL
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistatic char *
4562306a36Sopenharmony_ci__demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int rlen = 0;
4862306a36Sopenharmony_ci	int array = 0;
4962306a36Sopenharmony_ci	int narg = 0;
5062306a36Sopenharmony_ci	const char *q;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!end)
5362306a36Sopenharmony_ci		end = str + strlen(str);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	for (q = str; q != end; q++) {
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		if (rlen == (maxlen - 1))
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		switch (*q) {
6162306a36Sopenharmony_ci		case 'L':
6262306a36Sopenharmony_ci			if (mode == MODE_PREFIX || mode == MODE_TYPE) {
6362306a36Sopenharmony_ci				if (mode == MODE_TYPE) {
6462306a36Sopenharmony_ci					if (narg)
6562306a36Sopenharmony_ci						rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
6662306a36Sopenharmony_ci					narg++;
6762306a36Sopenharmony_ci				}
6862306a36Sopenharmony_ci				if (mode == MODE_PREFIX)
6962306a36Sopenharmony_ci					mode = MODE_CLASS;
7062306a36Sopenharmony_ci				else
7162306a36Sopenharmony_ci					mode = MODE_CTYPE;
7262306a36Sopenharmony_ci			} else
7362306a36Sopenharmony_ci				buf[rlen++] = *q;
7462306a36Sopenharmony_ci			break;
7562306a36Sopenharmony_ci		case 'B':
7662306a36Sopenharmony_ci		case 'C':
7762306a36Sopenharmony_ci		case 'D':
7862306a36Sopenharmony_ci		case 'F':
7962306a36Sopenharmony_ci		case 'I':
8062306a36Sopenharmony_ci		case 'J':
8162306a36Sopenharmony_ci		case 'S':
8262306a36Sopenharmony_ci		case 'Z':
8362306a36Sopenharmony_ci			if (mode == MODE_TYPE) {
8462306a36Sopenharmony_ci				if (narg)
8562306a36Sopenharmony_ci					rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
8662306a36Sopenharmony_ci				rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']);
8762306a36Sopenharmony_ci				while (array--)
8862306a36Sopenharmony_ci					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
8962306a36Sopenharmony_ci				array = 0;
9062306a36Sopenharmony_ci				narg++;
9162306a36Sopenharmony_ci			} else
9262306a36Sopenharmony_ci				buf[rlen++] = *q;
9362306a36Sopenharmony_ci			break;
9462306a36Sopenharmony_ci		case 'V':
9562306a36Sopenharmony_ci			if (mode == MODE_TYPE) {
9662306a36Sopenharmony_ci				rlen += scnprintf(buf + rlen, maxlen - rlen, "void");
9762306a36Sopenharmony_ci				while (array--)
9862306a36Sopenharmony_ci					rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
9962306a36Sopenharmony_ci				array = 0;
10062306a36Sopenharmony_ci			} else
10162306a36Sopenharmony_ci				buf[rlen++] = *q;
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci		case '[':
10462306a36Sopenharmony_ci			if (mode != MODE_TYPE)
10562306a36Sopenharmony_ci				goto error;
10662306a36Sopenharmony_ci			array++;
10762306a36Sopenharmony_ci			break;
10862306a36Sopenharmony_ci		case '(':
10962306a36Sopenharmony_ci			if (mode != MODE_FUNC)
11062306a36Sopenharmony_ci				goto error;
11162306a36Sopenharmony_ci			buf[rlen++] = *q;
11262306a36Sopenharmony_ci			mode = MODE_TYPE;
11362306a36Sopenharmony_ci			break;
11462306a36Sopenharmony_ci		case ')':
11562306a36Sopenharmony_ci			if (mode != MODE_TYPE)
11662306a36Sopenharmony_ci				goto error;
11762306a36Sopenharmony_ci			buf[rlen++] = *q;
11862306a36Sopenharmony_ci			narg = 0;
11962306a36Sopenharmony_ci			break;
12062306a36Sopenharmony_ci		case ';':
12162306a36Sopenharmony_ci			if (mode != MODE_CLASS && mode != MODE_CTYPE)
12262306a36Sopenharmony_ci				goto error;
12362306a36Sopenharmony_ci			/* safe because at least one other char to process */
12462306a36Sopenharmony_ci			if (isalpha(*(q + 1)) && mode == MODE_CLASS)
12562306a36Sopenharmony_ci				rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
12662306a36Sopenharmony_ci			if (mode == MODE_CLASS)
12762306a36Sopenharmony_ci				mode = MODE_FUNC;
12862306a36Sopenharmony_ci			else if (mode == MODE_CTYPE)
12962306a36Sopenharmony_ci				mode = MODE_TYPE;
13062306a36Sopenharmony_ci			break;
13162306a36Sopenharmony_ci		case '/':
13262306a36Sopenharmony_ci			if (mode != MODE_CLASS && mode != MODE_CTYPE)
13362306a36Sopenharmony_ci				goto error;
13462306a36Sopenharmony_ci			rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
13562306a36Sopenharmony_ci			break;
13662306a36Sopenharmony_ci		default :
13762306a36Sopenharmony_ci			buf[rlen++] = *q;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	buf[rlen] = '\0';
14162306a36Sopenharmony_ci	return buf;
14262306a36Sopenharmony_cierror:
14362306a36Sopenharmony_ci	return NULL;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci/*
14762306a36Sopenharmony_ci * Demangle Java function signature (openJDK, not GCJ)
14862306a36Sopenharmony_ci * input:
14962306a36Sopenharmony_ci * 	str: string to parse. String is not modified
15062306a36Sopenharmony_ci *    flags: combination of JAVA_DEMANGLE_* flags to modify demangling
15162306a36Sopenharmony_ci * return:
15262306a36Sopenharmony_ci *	if input can be demangled, then a newly allocated string is returned.
15362306a36Sopenharmony_ci *	if input cannot be demangled, then NULL is returned
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * Note: caller is responsible for freeing demangled string
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cichar *
15862306a36Sopenharmony_cijava_demangle_sym(const char *str, int flags)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	char *buf, *ptr;
16162306a36Sopenharmony_ci	char *p;
16262306a36Sopenharmony_ci	size_t len, l1 = 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!str)
16562306a36Sopenharmony_ci		return NULL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* find start of return type */
16862306a36Sopenharmony_ci	p = strrchr(str, ')');
16962306a36Sopenharmony_ci	if (!p)
17062306a36Sopenharmony_ci		return NULL;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/*
17362306a36Sopenharmony_ci	 * expansion factor estimated to 3x
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci	len = strlen(str) * 3 + 1;
17662306a36Sopenharmony_ci	buf = malloc(len);
17762306a36Sopenharmony_ci	if (!buf)
17862306a36Sopenharmony_ci		return NULL;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	buf[0] = '\0';
18162306a36Sopenharmony_ci	if (!(flags & JAVA_DEMANGLE_NORET)) {
18262306a36Sopenharmony_ci		/*
18362306a36Sopenharmony_ci		 * get return type first
18462306a36Sopenharmony_ci		 */
18562306a36Sopenharmony_ci		ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE);
18662306a36Sopenharmony_ci		if (!ptr)
18762306a36Sopenharmony_ci			goto error;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		/* add space between return type and function prototype */
19062306a36Sopenharmony_ci		l1 = strlen(buf);
19162306a36Sopenharmony_ci		buf[l1++] = ' ';
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* process function up to return type */
19562306a36Sopenharmony_ci	ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX);
19662306a36Sopenharmony_ci	if (!ptr)
19762306a36Sopenharmony_ci		goto error;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return buf;
20062306a36Sopenharmony_cierror:
20162306a36Sopenharmony_ci	free(buf);
20262306a36Sopenharmony_ci	return NULL;
20362306a36Sopenharmony_ci}
204