247 lines
10 KiB
C++
247 lines
10 KiB
C++
#include "ASVariableFormatter.hpp"
|
|
#include <iostream>
|
|
|
|
DebugAdapterProtocol::Variable ASVariableFormatter::GetAsDAPVariable(asIScriptEngine* engine, asIScriptContext* ctx,
|
|
AngelscriptDebugger* debugger,
|
|
const std::string& name, int type, void* address) {
|
|
if ((type & asTYPEID_OBJHANDLE) != 0) {
|
|
address = *static_cast<void**>(address);
|
|
}
|
|
switch (type) {
|
|
case asTYPEID_BOOL: {
|
|
auto v = *static_cast<bool*>(address);
|
|
return {name, v ? "true" : "false", "bool"};
|
|
};
|
|
|
|
case asTYPEID_INT8: {
|
|
auto v = *static_cast<int8_t*>(address);
|
|
return {name, std::to_string(v), "int8"};
|
|
};
|
|
case asTYPEID_INT16: {
|
|
auto v = *static_cast<int16_t*>(address);
|
|
return {name, std::to_string(v), "int16"};
|
|
}
|
|
case asTYPEID_INT32: {
|
|
auto v = *static_cast<int32_t*>(address);
|
|
return {name, std::to_string(v), "int32"};
|
|
};
|
|
case asTYPEID_INT64: {
|
|
auto v = *static_cast<int64_t*>(address);
|
|
return {name, std::to_string(v), "int64"};
|
|
};
|
|
|
|
case asTYPEID_UINT8: {
|
|
auto v = *static_cast<uint8_t*>(address);
|
|
return {name, std::to_string(v), "uint8"};
|
|
};
|
|
case asTYPEID_UINT16: {
|
|
auto v = *static_cast<uint16_t*>(address);
|
|
return {name, std::to_string(v), "uint16"};
|
|
};
|
|
case asTYPEID_UINT32: {
|
|
auto v = *static_cast<uint32_t*>(address);
|
|
return {name, std::to_string(v), "uint32"};
|
|
};
|
|
case asTYPEID_UINT64: {
|
|
auto v = *static_cast<uint64_t*>(address);
|
|
return {name, std::to_string(v), "uint64"};
|
|
};
|
|
|
|
case asTYPEID_FLOAT: {
|
|
auto v = *static_cast<float*>(address);
|
|
return {name, std::to_string(v), "float"};
|
|
};
|
|
case asTYPEID_DOUBLE: {
|
|
auto v = *static_cast<double*>(address);
|
|
return {name, std::to_string(v), "double"};
|
|
};
|
|
|
|
default: break;
|
|
}
|
|
if ((type & asTYPEID_SCRIPTOBJECT) != 0) {
|
|
auto* obj = static_cast<asIScriptObject*>(address);
|
|
std::string typeName = engine->GetTypeInfoById(type)->GetName();
|
|
if (obj == nullptr) {
|
|
return DebugAdapterProtocol::Variable::FromNull(name, typeName);
|
|
}
|
|
auto typeData = obj->GetObjectType();
|
|
std::string stringified = typeName + "(*" + std::to_string((size_t)address) + ")";
|
|
auto stringifiedFunc = typeData->GetMethodByDecl("string opImplConv()");
|
|
if (stringifiedFunc != nullptr) {
|
|
ctx->PushState();
|
|
ctx->Prepare(stringifiedFunc);
|
|
ctx->SetObject(address);
|
|
if (ctx->Execute() == asEXECUTION_FINISHED) {
|
|
stringified = *(std::string*)ctx->GetAddressOfReturnValue();
|
|
}
|
|
ctx->PopState();
|
|
}
|
|
|
|
size_t reference = 0;
|
|
if (obj->GetPropertyCount() > 0) {
|
|
std::cout << "Storing scriptObj " << name << std::endl;
|
|
obj->AddRef();
|
|
reference = debugger->StoreVariable(address, type, [](void* a) {
|
|
if (a != nullptr) {
|
|
((asIScriptObject*)a)->Release();
|
|
}
|
|
});
|
|
}
|
|
return DebugAdapterProtocol::Variable::FromPointer(name, typeName, stringified, reference);
|
|
}
|
|
// TODO: Caching of type id
|
|
if (type == engine->GetTypeIdByDecl("string")) {
|
|
auto* str = static_cast<std::string*>(address);
|
|
if (str == nullptr) {
|
|
return DebugAdapterProtocol::Variable::FromNull(name, "string");
|
|
}
|
|
return DebugAdapterProtocol::Variable::FromString(name, "\"" + *str + "\"");
|
|
}
|
|
auto* typeData = engine->GetTypeInfoById(type);
|
|
if ((typeData->GetFlags() & asOBJ_ENUM) != 0) {
|
|
const auto* enumValue = typeData->GetEnumValueByIndex(*static_cast<int32_t*>(address), nullptr);
|
|
return {name, enumValue, typeData->GetName()};
|
|
}
|
|
|
|
if (address == nullptr) {
|
|
return DebugAdapterProtocol::Variable::FromNull(name, typeData->GetName());
|
|
}
|
|
|
|
std::string stringified = std::string(typeData->GetName()) + "(*" + std::to_string((size_t)address) + ")";
|
|
auto stringifiedFunc = typeData->GetMethodByDecl("string opImplConv()");
|
|
if (stringifiedFunc != nullptr) {
|
|
ctx->Prepare(stringifiedFunc);
|
|
ctx->SetObject(*(void**)address);
|
|
if (ctx->Execute() == asEXECUTION_FINISHED) {
|
|
stringified = "\"" + *(std::string*)ctx->GetAddressOfReturnValue() + "\"";
|
|
}
|
|
}
|
|
|
|
size_t reference = 0;
|
|
size_t varCount = typeData->GetPropertyCount();
|
|
for (asUINT i = 0; i < typeData->GetMethodCount(); ++i) {
|
|
if (typeData->GetMethodByIndex(i, true)->IsProperty()) {
|
|
varCount++;
|
|
}
|
|
}
|
|
if (varCount > 0) {
|
|
|
|
// If we want to re-use an object, we need to make sure to add a reference to it, so it doesn't get accidentally
|
|
// cleaned up. This is currently somewhat clunky, we look up the AddRef behaviour, and execute that in a new
|
|
// context.
|
|
auto b = static_cast<asEBehaviours>(-1);
|
|
asIScriptFunction* releaseFunc = nullptr;
|
|
bool hasAddRef = false;
|
|
for (asUINT i = 0; i < typeData->GetBehaviourCount(); ++i) {
|
|
auto* f = typeData->GetBehaviourByIndex(i, &b);
|
|
if (b == asBEHAVE_ADDREF) {
|
|
// If we push state or re-use the active context, we might lose the reference to the object we have.
|
|
// To resolve this we need a new context.
|
|
auto* c = ctx->GetEngine()->CreateContext();
|
|
assert(c->Prepare(f) >= 0);
|
|
assert(c->SetObject(address) >= 0);
|
|
assert(c->Execute() >= 0);
|
|
c->Release();
|
|
hasAddRef = true;
|
|
}
|
|
if (b == asBEHAVE_RELEASE) {
|
|
releaseFunc = f;
|
|
}
|
|
}
|
|
if (!hasAddRef) {
|
|
releaseFunc = nullptr;
|
|
}
|
|
|
|
reference = debugger->StoreVariable(address, type, [engine, releaseFunc](void* a) {
|
|
if (releaseFunc != nullptr) {
|
|
auto* c = engine->CreateContext();
|
|
c->Prepare(releaseFunc);
|
|
c->SetObject(a);
|
|
c->Execute();
|
|
c->Release();
|
|
}
|
|
});
|
|
}
|
|
return DebugAdapterProtocol::Variable::FromPointer(name, typeData->GetName(), stringified, reference);
|
|
}
|
|
|
|
template <typename T, int (asIScriptContext::*SetArg)(asUINT, T), T (asIScriptContext::*GetLength)()>
|
|
void ASVariableFormatter::FormatArrayLike(std::vector<DebugAdapterProtocol::Variable>& vars, asIScriptEngine* engine,
|
|
asIScriptContext* ctx, void* address, AngelscriptDebugger* debugger,
|
|
asIScriptFunction* indexFunc) {
|
|
auto length = (ctx->*GetLength)();
|
|
for (T i = 0; i < length; ++i) {
|
|
assert(ctx->Prepare(indexFunc) >= 0);
|
|
ctx->SetObject(address);
|
|
(ctx->*SetArg)(0, i);
|
|
ctx->Execute();
|
|
auto name = std::to_string(i);
|
|
vars.push_back(GetAsDAPVariable(engine, ctx, debugger, name, indexFunc->GetReturnTypeId(),
|
|
ctx->GetAddressOfReturnValue()));
|
|
}
|
|
}
|
|
|
|
void ASVariableFormatter::GetChildDAPVariables(std::vector<DebugAdapterProtocol::Variable>& vars,
|
|
asIScriptEngine* engine, asIScriptContext* ctx,
|
|
AngelscriptDebugger* debugger, int type, void* address) {
|
|
if (address == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if ((type & asTYPEID_SCRIPTOBJECT) != 0) {
|
|
auto* obj = static_cast<asIScriptObject*>(address);
|
|
for (asUINT i = 0; i < obj->GetPropertyCount(); ++i) {
|
|
const auto* propertyName = obj->GetPropertyName(i);
|
|
auto propertyType = obj->GetPropertyTypeId(i);
|
|
auto* propertyAddress = obj->GetAddressOfProperty(i);
|
|
vars.push_back(GetAsDAPVariable(engine, ctx, debugger, propertyName, propertyType, propertyAddress));
|
|
}
|
|
return;
|
|
}
|
|
|
|
auto* typeData = engine->GetTypeInfoById(type);
|
|
for (asUINT i = 0; i < typeData->GetPropertyCount(); ++i) {
|
|
const char* propertyName = nullptr;
|
|
int propertyType = 0;
|
|
int offset = 0;
|
|
|
|
typeData->GetProperty(i, &propertyName, &propertyType, nullptr, nullptr, &offset);
|
|
vars.push_back(
|
|
GetAsDAPVariable(engine, ctx, debugger, propertyName, propertyType, (void*)((size_t)address + offset)));
|
|
}
|
|
for (asUINT i = 0; i < typeData->GetMethodCount(); ++i) {
|
|
auto func = typeData->GetMethodByIndex(i, true);
|
|
if (func->IsProperty()) {
|
|
assert(ctx->Prepare(func) >= 0);
|
|
ctx->SetObject(address);
|
|
auto state = ctx->Execute();
|
|
if (state == asEXECUTION_FINISHED) {
|
|
auto name = std::string(func->GetName()).substr(4);
|
|
vars.push_back(GetAsDAPVariable(engine, ctx, debugger, name, func->GetReturnTypeId(),
|
|
ctx->GetAddressOfReturnValue()));
|
|
}
|
|
}
|
|
}
|
|
|
|
auto indexFunc = typeData->GetMethodByName("get_opIndex", true);
|
|
if (indexFunc != nullptr && indexFunc->GetParamCount() == 1) {
|
|
auto lengthFunc = typeData->GetMethodByName("get_Length", true);
|
|
int t;
|
|
indexFunc->GetParam(0, &t);
|
|
if (lengthFunc != nullptr && lengthFunc->GetParamCount() == 0 && lengthFunc->GetReturnTypeId() == t) {
|
|
assert(ctx->Prepare(lengthFunc) >= 0);
|
|
ctx->SetObject(address);
|
|
ctx->Execute();
|
|
if (t == asTYPEID_UINT64) {
|
|
FormatArrayLike<asQWORD, &asIScriptContext::SetArgQWord, &asIScriptContext::GetReturnQWord>(
|
|
vars, engine, ctx, address, debugger, indexFunc);
|
|
}
|
|
else if (t == asTYPEID_INT32){
|
|
FormatArrayLike<asDWORD, &asIScriptContext::SetArgDWord, &asIScriptContext::GetReturnDWord>(
|
|
vars, engine, ctx, address, debugger, indexFunc);
|
|
}
|
|
}
|
|
// vars.push_back(DebugAdapterProtocol::Variable("has_indexer", "true", "string", {}));
|
|
}
|
|
}
|