* Copyright 2008-2009, Axel Dörfler, axeld@pinc-software.de.
* This file may be used under the terms of the MIT License.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <TypeConstants.h>
#ifdef _KERNEL_MODE
# include <debug.h>
#endif
#include "demangle.h"
#ifdef TRACE_GCC2_DEMANGLER
# include <stdio.h>
# define TRACE(x...) printf(x)
#else
# define TRACE(x...) ;
#endif
class Input {
public:
Input(const char* string, size_t length)
:
fString(string),
fLength(length)
{
}
const char* String() const
{
return fString;
}
int CharsRemaining() const
{
return fLength;
}
void Skip(size_t count)
{
if (count > fLength) {
TRACE("Input::Skip(): fOffset > fLength\n");
fString += fLength;
fLength = 0;
return;
}
fString += count;
fLength -= count;
}
Input& operator++()
{
Skip(1);
return *this;
}
Input operator++(int)
{
Input result(*this);
++(*this);
return result;
}
char operator[](size_t index) const
{
if (index >= fLength) {
TRACE("Input::operator[](): fOffset + index >= fLength\n");
return '\0';
}
return fString[index];
}
private:
const char* fString;
size_t fLength;
};
enum {
TYPE_FUNCTION,
TYPE_METHOD,
};
static void
ignore_qualifiers(Input& _arg)
{
while (isupper(_arg[0])) {
if (_arg[0] == 'Q') {
break;
}
if (_arg[0] == 'F') {
while (_arg.CharsRemaining() && _arg[0] != '_')
_arg++;
if (!_arg.CharsRemaining())
break;
}
_arg++;
}
}
static uint32
argument_type(Input arg, size_t& length)
{
length = sizeof(int);
switch (char type = arg[0]) {
case 'P':
case 'R':
length = sizeof(void*);
ignore_qualifiers(arg);
if (arg[0] == 'c')
return B_STRING_TYPE;
if (arg[0] == 't') {
return 0;
}
return type == 'P' ? B_POINTER_TYPE : B_REF_TYPE;
case 'x':
length = sizeof(long long);
return B_INT64_TYPE;
case 'l':
if (sizeof(long) == 4)
return B_INT32_TYPE;
return B_INT64_TYPE;
case 'i':
return B_INT32_TYPE;
case 's':
return B_INT16_TYPE;
case 'c':
return B_INT8_TYPE;
case 'b':
return B_BOOL_TYPE;
case 'U':
switch (arg[1]) {
case 'x':
length = sizeof(long long);
return B_UINT64_TYPE;
case 'l':
if (sizeof(long) == 4)
return B_UINT32_TYPE;
return B_UINT64_TYPE;
case 'i':
return B_UINT32_TYPE;
case 's':
return B_UINT16_TYPE;
case 'c':
return B_UINT8_TYPE;
default:
return B_UINT32_TYPE;
}
break;
case 'f':
return B_FLOAT_TYPE;
case 'd':
length = sizeof(double);
return B_DOUBLE_TYPE;
case 'r':
return 0;
case 't':
return 0;
default:
return B_ANY_TYPE;
}
}
static uint32
parse_number(Input& _arg, bool numberLeft)
{
Input arg = _arg;
while (isdigit(arg[0]))
arg++;
uint32 value;
if (arg[0] == '_' && (!numberLeft || isdigit(arg[1]))) {
const char* tmp_arg = _arg.String();
value = strtoul(tmp_arg, (char**)&tmp_arg, 10);
_arg.Skip(tmp_arg - _arg.String());
if (_arg[0] == '_')
_arg++;
} else {
value = _arg[0] - '0';
_arg++;
}
return value;
}
static uint32
parse_repeats(Input& _arg, uint32& index)
{
if (_arg[0] != 'N')
return 0;
_arg++;
uint32 count = parse_number(_arg, true);
index = parse_number(_arg, false);
return count;
}
static void
skip_numbers(Input& _arg, int32 count)
{
_arg++;
while (count-- > 0) {
parse_number(_arg, count != 0);
}
}
static uint32
count_namespaces(Input& _mangled)
{
Input mangled = _mangled;
int32 namespaces = 0;
if (mangled[0] == 'Q') {
if (mangled[1] == '_') {
const char* mangled_str = mangled.String();
namespaces = strtoul(mangled_str + 2, (char**)&mangled_str, 10);
mangled.Skip(mangled_str - mangled.String());
if (mangled[0] != '_')
namespaces = 0;
mangled++;
} else {
namespaces = mangled[1] - '0';
mangled.Skip(2);
}
} else if (isdigit(mangled[0]))
namespaces = 1;
_mangled = mangled;
return namespaces;
}
static void
skip_namespaces(Input& mangled)
{
int32 namespaces = count_namespaces(mangled);
while (namespaces-- > 0) {
if (!isdigit(mangled[0]))
break;
const char* mangled_str = mangled.String();
mangled_str += strtoul(mangled_str, (char**)&mangled_str, 10);
mangled.Skip(mangled_str - mangled.String());
}
}
static bool
has_named_argument(Input& arg)
{
ignore_qualifiers(arg);
return arg[0] == 'Q' || isdigit(arg[0]);
}
static uint32
argument_length(Input& _arg)
{
if (_arg[0] == 'N') {
skip_numbers(_arg, 2);
return 0;
} else if (_arg[0] == 'T') {
skip_numbers(_arg, 1);
return 0;
}
ignore_qualifiers(_arg);
if (!_arg.CharsRemaining())
return 0;
if (_arg[0] != 'Q' && !isdigit(_arg[0]))
return 1;
Input mangled = _arg;
skip_namespaces(mangled);
return mangled.String() - _arg.String();
}
static Input
next_argument(Input arg)
{
if (!arg.CharsRemaining() || !arg[0])
return arg;
uint32 length = argument_length(arg);
arg.Skip(length);
return arg;
}
static Input
first_argument(Input mangled)
{
skip_namespaces(mangled);
return mangled;
}
static bool
mangled_start(Input& name, size_t* _symbolLength, int32* _symbolType)
{
size_t pos = name.CharsRemaining() - 1;
bool foundStart = false;
while (pos > 1) {
if (name[pos] == '_') {
if (name[pos - 1] == '_') {
foundStart = true;
name.Skip(pos + 1);
break;
} else
pos--;
}
pos--;
}
if (!foundStart)
return false;
if (name[0] == 'H') {
return false;
}
if (_symbolLength != NULL)
*_symbolLength = pos - 1;
if (name[0] == 'F') {
if (_symbolType != NULL)
*_symbolType = TYPE_FUNCTION;
name.Skip(1);
return true;
}
if (_symbolType != NULL)
*_symbolType = TYPE_METHOD;
return true;
}
static status_t
get_next_argument_internal(uint32* _cookie, const char* symbol, char* name,
size_t nameSize, int32* _type, size_t* _argumentLength, bool repeating)
{
Input mangled(symbol, strlen(symbol));
if (!mangled_start(mangled, NULL, NULL))
return B_BAD_VALUE;
Input arg = first_argument(mangled);
if (arg[0] == 'v')
return B_ENTRY_NOT_FOUND;
uint32 current = *_cookie;
if (current > 32)
return B_TOO_MANY_ARGS;
for (uint32 i = 0; i < current; i++) {
arg = next_argument(arg);
if (arg.CharsRemaining() && arg[0] == 'N') {
uint32 index;
uint32 count = parse_repeats(arg, index);
if (current <= i + count) {
if (repeating)
return B_LINK_LIMIT;
status_t status = get_next_argument_internal(&index, symbol,
name, nameSize, _type, _argumentLength, true);
if (status == B_OK)
(*_cookie)++;
return status;
}
i += count - 1;
}
}
if (arg[0] == '\0' || !arg.CharsRemaining())
return B_ENTRY_NOT_FOUND;
TRACE("\n\targ %ld: %s\n", current, arg.String());
if (arg[0] == 'T') {
if (repeating)
return B_LINK_LIMIT;
arg++;
uint32 index = parse_number(arg, false);
status_t status = get_next_argument_internal(&index, symbol, name,
nameSize, _type, _argumentLength, true);
if (status == B_OK)
(*_cookie)++;
return status;
}
(*_cookie)++;
size_t argumentLength;
int32 type = argument_type(arg, argumentLength);
if (type == 0)
return B_NOT_SUPPORTED;
if (_type != NULL)
*_type = type;
if (_argumentLength != NULL)
*_argumentLength = argumentLength;
name[0] = '\0';
if (!has_named_argument(arg))
return B_OK;
Input namespaceStart = arg;
int32 namespaces = count_namespaces(namespaceStart);
while (namespaces-- > 0) {
if (namespaceStart[0] == 't') {
return B_BAD_TYPE;
}
if (!isdigit(namespaceStart[0]))
break;
const char* ns_str = namespaceStart.String();
uint32 length = strtoul(ns_str, (char**)&ns_str, 10);
namespaceStart.Skip(ns_str - namespaceStart.String());
uint32 max = strlen(name) + length + 1;
strlcat(name, namespaceStart.String(), min_c(max, nameSize));
if (namespaces > 0)
strlcat(name, "::", nameSize);
namespaceStart.Skip(length);
}
return B_OK;
}
const char*
demangle_symbol_gcc2(const char* name, char* buffer, size_t bufferSize,
bool* _isObjectMethod)
{
if (name == NULL)
return NULL;
size_t nameLength;
int32 type;
Input mangled(name, strlen(name));
if (!mangled_start(mangled, &nameLength, &type))
return NULL;
if (mangled[0] == 'C') {
type = TYPE_METHOD;
mangled++;
}
if (_isObjectMethod != NULL) {
*_isObjectMethod = type == TYPE_METHOD;
}
Input namespaceStart = mangled;
int32 namespaces = count_namespaces(namespaceStart);
buffer[0] = '\0';
while (namespaces-- > 0) {
if (namespaceStart[0] == 't') {
return NULL;
}
if (!isdigit(namespaceStart[0]))
break;
const char* ns_str = namespaceStart.String();
uint32 length = strtoul(ns_str, (char**)&ns_str, 10);
namespaceStart.Skip(ns_str - namespaceStart.String());
uint32 max = strlen(buffer) + length + 1;
strlcat(buffer, namespaceStart.String(), min_c(max, bufferSize));
strlcat(buffer, "::", bufferSize);
namespaceStart.Skip(length);
}
size_t max = strlen(buffer) + nameLength + 1;
strlcat(buffer, name, min_c(max, bufferSize));
return buffer;
}
status_t
get_next_argument_gcc2(uint32* _cookie, const char* symbol, char* name,
size_t nameSize, int32* _type, size_t* _argumentLength)
{
status_t error = get_next_argument_internal(_cookie, symbol, name, nameSize,
_type, _argumentLength, false);
if (error != B_OK)
return error;
if (name[0] != '\0' && (*_type == B_POINTER_TYPE || *_type == B_REF_TYPE))
strlcat(name, *_type == B_POINTER_TYPE ? "*" : "&", nameSize);
return B_OK;
}