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 | |
13 | enum { |
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 |
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', "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 | */ |
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_TYPE) { |
63 | if (mode == MODE_TYPE) { |
64 | if (narg) |
65 | rlen += scnprintf(buf: buf + rlen, size: maxlen - rlen, fmt: ", " ); |
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: buf + rlen, size: maxlen - rlen, fmt: ", " ); |
86 | rlen += scnprintf(buf: buf + rlen, size: maxlen - rlen, fmt: "%s" , base_types[*q - 'A']); |
87 | while (array--) |
88 | rlen += scnprintf(buf: buf + rlen, size: maxlen - rlen, fmt: "[]" ); |
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: buf + rlen, size: maxlen - rlen, fmt: "void" ); |
97 | while (array--) |
98 | rlen += scnprintf(buf: buf + rlen, size: maxlen - rlen, fmt: "[]" ); |
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: buf + rlen, size: maxlen - rlen, fmt: "." ); |
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: buf + rlen, size: maxlen - rlen, fmt: "." ); |
135 | break; |
136 | default : |
137 | buf[rlen++] = *q; |
138 | } |
139 | } |
140 | buf[rlen] = '\0'; |
141 | return buf; |
142 | error: |
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 | */ |
157 | char * |
158 | java_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(str: p + 1, NULL, buf, maxlen: len, mode: 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, end: p + 1, buf: buf + l1, maxlen: len - l1, mode: MODE_PREFIX); |
196 | if (!ptr) |
197 | goto error; |
198 | |
199 | return buf; |
200 | error: |
201 | free(buf); |
202 | return NULL; |
203 | } |
204 | |