162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <string.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include "util/string2.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "demangle-ocaml.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/ctype.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic const char *caml_prefix = "caml";
1162306a36Sopenharmony_cistatic const size_t caml_prefix_len = 4;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* mangled OCaml symbols start with "caml" followed by an upper-case letter */
1462306a36Sopenharmony_cistatic bool
1562306a36Sopenharmony_ciocaml_is_mangled(const char *sym)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return 0 == strncmp(sym, caml_prefix, caml_prefix_len)
1862306a36Sopenharmony_ci		&& isupper(sym[caml_prefix_len]);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * input:
2362306a36Sopenharmony_ci *     sym: a symbol which may have been mangled by the OCaml compiler
2462306a36Sopenharmony_ci * return:
2562306a36Sopenharmony_ci *     if the input doesn't look like a mangled OCaml symbol, NULL is returned
2662306a36Sopenharmony_ci *     otherwise, a newly allocated string containing the demangled symbol is returned
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cichar *
2962306a36Sopenharmony_ciocaml_demangle_sym(const char *sym)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	char *result;
3262306a36Sopenharmony_ci	int j = 0;
3362306a36Sopenharmony_ci	int i;
3462306a36Sopenharmony_ci	int len;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (!ocaml_is_mangled(sym)) {
3762306a36Sopenharmony_ci		return NULL;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	len = strlen(sym);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* the demangled symbol is always smaller than the mangled symbol */
4362306a36Sopenharmony_ci	result = malloc(len + 1);
4462306a36Sopenharmony_ci	if (!result)
4562306a36Sopenharmony_ci		return NULL;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* skip "caml" prefix */
4862306a36Sopenharmony_ci	i = caml_prefix_len;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	while (i < len) {
5162306a36Sopenharmony_ci		if (sym[i] == '_' && sym[i + 1] == '_') {
5262306a36Sopenharmony_ci			/* "__" -> "." */
5362306a36Sopenharmony_ci			result[j++] = '.';
5462306a36Sopenharmony_ci			i += 2;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci		else if (sym[i] == '$' && isxdigit(sym[i + 1]) && isxdigit(sym[i + 2])) {
5762306a36Sopenharmony_ci			/* "$xx" is a hex-encoded character */
5862306a36Sopenharmony_ci			result[j++] = (hex(sym[i + 1]) << 4) | hex(sym[i + 2]);
5962306a36Sopenharmony_ci			i += 3;
6062306a36Sopenharmony_ci		}
6162306a36Sopenharmony_ci		else {
6262306a36Sopenharmony_ci			result[j++] = sym[i++];
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	result[j] = '\0';
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return result;
6862306a36Sopenharmony_ci}
69