/////////////////////////////////////////////////////////////////////////////
//
//  object.cpp - Extension to find Singularity object.
//
//  Copyright Microsoft Corporation.  All rights reserved.
//
#include "singx86.h"

#define VERBOSE 0

static bool isVtable(PSTR name,
                     __out PSTR fullclassname,
                     __out PSTR classname,
                     __out bool *vector,
                     __out bool *value)
{
    if (vector != NULL) {
        *vector = false;
    }
    if (value != NULL) {
        *value = false;
    }

    // Walk over the module name...
    while (*name && *name != '!') {
        *classname++ = *name;
        *fullclassname++ = *name++;
    }
    *classname = '\0';
    *fullclassname = '\0';

    if (*name != '!') {
        return false;
    }

    *classname++ = *name;
    *fullclassname++ = *name++;

    // Walk over the ClassVector_ if this is a vector type.
    if (strncmp(name, "ClassVector_", 12) == 0) {
        if (vector != NULL) {
            *vector = true;
        }
        name += 12;
    }

    // Walk over the Class_ or Struct_ prefix.
    if (strncmp(name, "Struct_", 7) == 0) {
        if (value != NULL) {
            *value = true;
        }
        name += 7;
    }
    else if (strncmp(name, "Class_", 6) == 0) {
        name += 6;
    }

    // Walk over the class name.
    PSTR classbeg = classname;

    while (*name && *name != ':') {
        if (*name == '_') {
            classname = classbeg;
            *fullclassname++ = *name++;
            continue;
        }
        else {
            *classname++ = *name;
            *fullclassname++ = *name++;
        }
    }
    *classname = '\0';
    *fullclassname = '\0';

    // Check if this is a vtable field.
    return ((strcmp(name, "::_vtable") == 0) ||
            (strcmp(name, "::`vftable'") == 0));
}

EXT_DECL(object) // Defines: PDEBUG_CLIENT Client, PCSTR args
{
    EXT_ENTER();    // Defines: HRESULT status = S_OK;
    UNREFERENCED_PARAMETER(args);

    ULONG64 address = 0;

    status = ExtEvalU64(&args, &address);
    if (status != S_OK) {
        goto Exit;
    }

    ULONG64 vtable = 0;
    EXT_CHECK(TraceReadPointer(1, address, &vtable));
    vtable &= ~(ULONG64)3;

    CHAR name[512];
    ULONG64 displacement = 0;
    status = g_ExtSymbols->GetNameByOffset(vtable,
                                           name,
                                           arrayof(name),
                                           NULL,
                                           &displacement);
    if (status != S_OK) {
        ExtErr("%p: No type information for vtable: [%p]\n", address, vtable);
        status = E_INVALIDARG;
        goto Exit;
    }

    if (displacement != 0) {
        ExtErr("%p: Invalid vtable %p, nearest symbol is %s+%I64d\n",
               address, vtable, name, displacement);
        status = E_INVALIDARG;
        goto Exit;
    }

    ExtVerb("%p: name %s\n", vtable, name);

    CHAR fullclassname[512];
    CHAR classname[128];
    bool vector = false;
    bool value = false;

    if (!isVtable(name, fullclassname, classname, &vector, &value)) {
        ExtErr("%p: Invalid symbol for vtable: %s\n", address, name);
        goto Exit;
    }

    ExtOut("%p: %s [vtable=%p]\n", address, classname, vtable);

    // TODO: We should automatically determine whether classname or
    // fullclassname is correct.

    CHAR command[1024];
    sprintf(command, "dt -n %s %s 0x%p", classname, args, address);
    ExtVerb("Command: %s\n", command);

    status = g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS |
                                   DEBUG_OUTCTL_OVERRIDE_MASK |
                                   DEBUG_OUTCTL_NOT_LOGGED,
                                   command,
                                   DEBUG_EXECUTE_DEFAULT );

    EXT_LEAVE();    // Macro includes: return status;
}
