root/tools/perf/util/demangle-java.c
// SPDX-License-Identifier: GPL-2.0
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "symbol.h"

#include "demangle-java.h"

#include <linux/ctype.h>
#include <linux/kernel.h>

enum {
        MODE_PREFIX = 0,
        MODE_CLASS  = 1,
        MODE_FUNC   = 2,
        MODE_TYPE   = 3,
        MODE_CTYPE  = 4, /* class arg */
};

#define BASE_ENT(c, n)  [c - 'A']=n
static const char *base_types['Z' - 'A' + 1] = {
        BASE_ENT('B', "byte" ),
        BASE_ENT('C', "char" ),
        BASE_ENT('D', "double" ),
        BASE_ENT('F', "float" ),
        BASE_ENT('I', "int" ),
        BASE_ENT('J', "long" ),
        BASE_ENT('S', "short" ),
        BASE_ENT('Z', "boolean" ),
};

/*
 * demangle Java symbol between str and end positions and stores
 * up to maxlen characters into buf. The parser starts in mode.
 *
 * Use MODE_PREFIX to process entire prototype till end position
 * Use MODE_TYPE to process return type if str starts on return type char
 *
 *  Return:
 *      success: buf
 *      error  : NULL
 */
static char *
__demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode)
{
        int rlen = 0;
        int array = 0;
        int narg = 0;
        const char *q;

        if (!end)
                end = str + strlen(str);

        for (q = str; q != end; q++) {

                if (rlen == (maxlen - 1))
                        break;

                switch (*q) {
                case 'L':
                        if (mode == MODE_PREFIX || mode == MODE_TYPE) {
                                if (mode == MODE_TYPE) {
                                        if (narg)
                                                rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
                                        narg++;
                                }
                                if (mode == MODE_PREFIX)
                                        mode = MODE_CLASS;
                                else
                                        mode = MODE_CTYPE;
                        } else
                                buf[rlen++] = *q;
                        break;
                case 'B':
                case 'C':
                case 'D':
                case 'F':
                case 'I':
                case 'J':
                case 'S':
                case 'Z':
                        if (mode == MODE_TYPE) {
                                if (narg)
                                        rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
                                rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']);
                                while (array--)
                                        rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
                                array = 0;
                                narg++;
                        } else
                                buf[rlen++] = *q;
                        break;
                case 'V':
                        if (mode == MODE_TYPE) {
                                rlen += scnprintf(buf + rlen, maxlen - rlen, "void");
                                while (array--)
                                        rlen += scnprintf(buf + rlen, maxlen - rlen, "[]");
                                array = 0;
                        } else
                                buf[rlen++] = *q;
                        break;
                case '[':
                        if (mode != MODE_TYPE)
                                goto error;
                        array++;
                        break;
                case '(':
                        if (mode != MODE_FUNC)
                                goto error;
                        buf[rlen++] = *q;
                        mode = MODE_TYPE;
                        break;
                case ')':
                        if (mode != MODE_TYPE)
                                goto error;
                        buf[rlen++] = *q;
                        narg = 0;
                        break;
                case ';':
                        if (mode != MODE_CLASS && mode != MODE_CTYPE)
                                goto error;
                        /* safe because at least one other char to process */
                        if (isalpha(*(q + 1)) && mode == MODE_CLASS)
                                rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
                        if (mode == MODE_CLASS)
                                mode = MODE_FUNC;
                        else if (mode == MODE_CTYPE)
                                mode = MODE_TYPE;
                        break;
                case '/':
                        if (mode != MODE_CLASS && mode != MODE_CTYPE)
                                goto error;
                        rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
                        break;
                default :
                        buf[rlen++] = *q;
                }
        }
        buf[rlen] = '\0';
        return buf;
error:
        return NULL;
}

/*
 * Demangle Java function signature (openJDK, not GCJ)
 * input:
 *      str: string to parse. String is not modified
 *    flags: combination of JAVA_DEMANGLE_* flags to modify demangling
 * return:
 *      if input can be demangled, then a newly allocated string is returned.
 *      if input cannot be demangled, then NULL is returned
 *
 * Note: caller is responsible for freeing demangled string
 */
char *
java_demangle_sym(const char *str, int flags)
{
        char *buf, *ptr;
        const char *p;
        size_t len, l1 = 0;

        if (!str)
                return NULL;

        /* find start of return type */
        p = strrchr(str, ')');
        if (!p)
                return NULL;

        /*
         * expansion factor estimated to 3x
         */
        len = strlen(str) * 3 + 1;
        buf = malloc(len);
        if (!buf)
                return NULL;

        buf[0] = '\0';
        if (!(flags & JAVA_DEMANGLE_NORET)) {
                /*
                 * get return type first
                 */
                ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE);
                if (!ptr)
                        goto error;

                /* add space between return type and function prototype */
                l1 = strlen(buf);
                buf[l1++] = ' ';
        }

        /* process function up to return type */
        ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX);
        if (!ptr)
                goto error;

        return buf;
error:
        free(buf);
        return NULL;
}