mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
8355 lines
230 KiB
C++
8355 lines
230 KiB
C++
#include "DbgExprEvaluator.h"
|
|
#include "WinDebugger.h"
|
|
#include "Compiler/BfUtil.h"
|
|
#include "Compiler/BfSystem.h"
|
|
#include "Compiler/BfParser.h"
|
|
#include "Compiler/BfReducer.h"
|
|
#include "Compiler/BfPrinter.h"
|
|
#include "Compiler/BfDemangler.h"
|
|
#include "DWARFInfo.h"
|
|
#include "DebugManager.h"
|
|
#include "DebugVisualizers.h"
|
|
#include "BeefySysLib/util/BeefPerf.h"
|
|
#include "BeefySysLib/util/StackHelper.h"
|
|
|
|
#include "BeefySysLib/util/AllocDebug.h"
|
|
|
|
USING_NS_BF_DBG;
|
|
using namespace llvm;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DwMethodMatcher::DwMethodMatcher(BfAstNode* targetSrc, DbgExprEvaluator* exprEvaluator, const StringImpl& methodName, SizedArrayImpl<DbgTypedValue>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments) :
|
|
mArguments(arguments)
|
|
{
|
|
mTargetSrc = targetSrc;
|
|
mExprEvaluator = exprEvaluator;
|
|
mMethodName = methodName;
|
|
mArguments = arguments;
|
|
mBestMethodDef = NULL;
|
|
mBackupMethodDef = NULL;
|
|
mBestMethodTypeInstance = NULL;
|
|
mExplicitInterfaceCheck = NULL;
|
|
mHadExplicitGenericArguments = false;
|
|
mTargetIsConst = false;
|
|
|
|
if (methodGenericArguments != NULL)
|
|
{
|
|
for (auto genericArg : *methodGenericArguments)
|
|
{
|
|
if (genericArg == NULL)
|
|
return;
|
|
auto genericArgType = mExprEvaluator->ResolveTypeRef(genericArg);
|
|
if (genericArgType == NULL)
|
|
return;
|
|
mBestMethodGenericArguments.push_back(genericArgType);
|
|
//mBestMethodGenericArgumentSrcs.push_back(genericArg);
|
|
}
|
|
mHadExplicitGenericArguments = true;
|
|
}
|
|
}
|
|
|
|
/*bool DwMethodMatcher::InferGenericArgument(DbgType* argType, DbgType* wantType)
|
|
{
|
|
if (argType == NULL)
|
|
return false;
|
|
|
|
if (wantType->IsGenericParam())
|
|
{
|
|
auto wantGenericParam = (BfGenericParamType*)wantType;
|
|
if (wantGenericParam->mGenericParamKind == BfGenericParamKind_Method)
|
|
{
|
|
auto prevGenericMethodArg = mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx];
|
|
if (prevGenericMethodArg == NULL)
|
|
{
|
|
mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = argType;
|
|
return true;
|
|
}
|
|
|
|
// Prev is already best
|
|
if (mModule->CanCast(DbgTypedValue(NULL, argType), prevGenericMethodArg))
|
|
return true;
|
|
|
|
// New best?
|
|
if (mModule->CanCast(DbgTypedValue(NULL, prevGenericMethodArg), argType))
|
|
{
|
|
mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = argType;
|
|
return true;
|
|
}
|
|
|
|
// No implicit conversion, FAIL!
|
|
mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (wantType->IsGenericTypeInstance())
|
|
{
|
|
if (!argType->IsGenericTypeInstance())
|
|
return true;
|
|
auto wantGenericType = (BfTypeInstance*)wantType;
|
|
auto argGenericType = (BfTypeInstance*)argType;
|
|
if (argGenericType->mTypeDef != wantGenericType->mTypeDef)
|
|
return true;
|
|
|
|
for (int genericArgIdx = 0; genericArgIdx < (int)argGenericType->mTypeGenericArguments.size(); genericArgIdx++)
|
|
InferGenericArgument(argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericType->mTypeGenericArguments[genericArgIdx]);
|
|
return true;
|
|
}
|
|
|
|
if (wantType->IsRef())
|
|
{
|
|
if (!argType->IsRef())
|
|
return true;
|
|
auto wantRefType = (BfRefType*)wantType;
|
|
auto argRefType = (BfRefType*)argType;
|
|
if (wantRefType->mIsOut != argRefType->mIsOut)
|
|
return true;
|
|
InferGenericArgument(argRefType->mElementType, wantRefType->mElementType);
|
|
return true;
|
|
}
|
|
|
|
if (wantType->IsPointer())
|
|
{
|
|
if (!argType->IsPointer())
|
|
return true;
|
|
auto wantPointerType = (BfPointerType*) wantType;
|
|
auto argPointerType = (BfPointerType*) argType;
|
|
InferGenericArgument(argPointerType->mElementType, wantPointerType->mElementType);
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}*/
|
|
|
|
void DwMethodMatcher::CompareMethods(DbgSubprogram* prevMethodInstance, DwTypeVector* prevGenericArgumentsSubstitute,
|
|
DbgSubprogram* newMethodInstance, DwTypeVector* genericArgumentsSubstitute,
|
|
bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail)
|
|
{
|
|
int numUsedParams = 0;
|
|
int prevNumUsedParams = 0;
|
|
bool usedExtendedForm = false;
|
|
bool prevUsedExtendedForm = false;
|
|
|
|
bool isBetter = false;
|
|
bool isWorse = false;
|
|
int argIdx = 0;
|
|
|
|
DbgSubprogram* prevMethodDef = prevMethodInstance;
|
|
DbgSubprogram* newMethodDef = newMethodInstance;
|
|
|
|
#define SET_BETTER_OR_WORSE(lhs, rhs) \
|
|
if ((lhs) && !(rhs)) isBetter = true; \
|
|
if (!(lhs) && (rhs)) isWorse = true;
|
|
|
|
int newArgOffset = newMethodInstance->mHasThis ? 1 : 0;
|
|
int prevArgOffset = prevMethodInstance->mHasThis ? 1 : 0;
|
|
|
|
DbgVariable* param = newMethodInstance->mParams.mHead;
|
|
if (newMethodInstance->mHasThis)
|
|
param = param->mNext;
|
|
DbgVariable* prevParam = prevMethodInstance->mParams.mHead;
|
|
if (prevMethodInstance->mHasThis)
|
|
prevParam = prevParam->mNext;
|
|
|
|
for (argIdx = 0; argIdx < (int) mArguments.size(); argIdx++)
|
|
{
|
|
DbgTypedValue arg = mArguments[argIdx];
|
|
DbgType* paramType = param->mType;
|
|
DbgType* prevParamType = prevParam->mType;
|
|
|
|
numUsedParams++;
|
|
prevNumUsedParams++;
|
|
|
|
/*if ((genericArgumentsSubstitute != NULL) && (paramType->IsUnspecializedType()))
|
|
paramType = mModule->SpecializeMethodType(paramType, *genericArgumentsSubstitute, allowSpecializeFail);
|
|
if (prevParamType->IsUnspecializedType())
|
|
prevParamType = mModule->SpecializeMethodType(prevParamType, *prevGenericArgumentsSubstitute, allowSpecializeFail);*/
|
|
|
|
//paramType = paramType->RemoveModifiers();
|
|
//prevParamType = prevParamType->RemoveModifiers();
|
|
|
|
if (paramType != prevParamType)
|
|
{
|
|
/*if ((paramType->IsGenericParam()) || (prevParamType->IsGenericParam()))
|
|
{
|
|
BF_ASSERT(allowSpecializeFail);
|
|
if (!prevParamType->IsGenericParam())
|
|
isBetter = true;
|
|
else if (!paramType->IsGenericParam())
|
|
isWorse = true;
|
|
}
|
|
else*/ if ((paramType->IsInteger()) && (prevParamType->IsInteger()))
|
|
{
|
|
if (paramType == arg.mType)
|
|
isBetter = true;
|
|
else if (prevParamType == arg.mType)
|
|
isWorse = true;
|
|
else
|
|
{
|
|
if (paramType->mSize < prevParamType->mSize)
|
|
isBetter = true;
|
|
else if (paramType->mSize > prevParamType->mSize)
|
|
isWorse = true;
|
|
else if (paramType->IsSigned())
|
|
isBetter = true;
|
|
else
|
|
isWorse = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mExprEvaluator->CanCast(DbgTypedValue::GetValueless(paramType), prevParamType))
|
|
isBetter = true;
|
|
if (mExprEvaluator->CanCast(DbgTypedValue::GetValueless(prevParamType), paramType))
|
|
isWorse = true;
|
|
}
|
|
}
|
|
|
|
/*if (newMethodDef->mParams[argIdx]->mParamType == BfParamType_Params)
|
|
usedExtendedForm = true;
|
|
if (prevMethodDef->mParams[argIdx]->mParamType == BfParamType_Params)
|
|
prevUsedExtendedForm = true;*/
|
|
|
|
if ((usedExtendedForm) || (prevUsedExtendedForm))
|
|
break;
|
|
|
|
param = param->mNext;
|
|
prevParam = prevParam->mNext;
|
|
}
|
|
|
|
// Check for unused extended params as next param - that still counts as using extended form
|
|
/*if ((argIdx < (int) newMethodDef->mParams.size()) && (newMethodDef->mParams[argIdx]->mParamType == BfParamType_Params))
|
|
usedExtendedForm = true;
|
|
if ((argIdx < (int) prevMethodDef->mParams.size()) && (prevMethodDef->mParams[argIdx]->mParamType == BfParamType_Params))
|
|
prevUsedExtendedForm = true;*/
|
|
|
|
// Non-generic trumps generic
|
|
/*if ((!isBetter) && (!isWorse))
|
|
{
|
|
SET_BETTER_OR_WORSE(newMethodInstance->mMethodGenericArguments.size() == 0, prevMethodInstance->mMethodGenericArguments.size() == 0);
|
|
}*/
|
|
|
|
// Normal form trumps extended form
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
SET_BETTER_OR_WORSE(!usedExtendedForm, !prevUsedExtendedForm);
|
|
}
|
|
|
|
// More used params trumps less params
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
int paramDiff = (int) numUsedParams - (int) prevNumUsedParams;
|
|
SET_BETTER_OR_WORSE(paramDiff > 0, paramDiff < 0);
|
|
}
|
|
|
|
// Fewer defaults trumps more defaults
|
|
/*if ((!isBetter) && (!isWorse))
|
|
{
|
|
// Since we know the number of used params is the same (previous check), we infer that the rest are defaults
|
|
int paramDiff = (int) newMethodInstance->mParamTypes.size() - (int) prevMethodInstance->mParamTypes.size();
|
|
SET_BETTER_OR_WORSE(paramDiff < 0, paramDiff > 0);
|
|
}*/
|
|
|
|
// Check specificity of args
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
param = newMethodInstance->mParams.mHead;
|
|
if (newMethodInstance->mHasThis)
|
|
param = param->mNext;
|
|
prevParam = prevMethodInstance->mParams.mHead;
|
|
if (prevMethodInstance->mHasThis)
|
|
prevParam = prevParam->mNext;
|
|
|
|
for (argIdx = 0; argIdx < (int) mArguments.size(); argIdx++)
|
|
{
|
|
DbgType* paramType = param->mType;
|
|
DbgType* prevParamType = prevParam->mType;
|
|
|
|
//TODO:
|
|
//isBetter |= mModule->IsTypeMoreSpecific(paramType, prevParamType);
|
|
//isWorse |= mModule->IsTypeMoreSpecific(prevParamType, paramType);
|
|
|
|
param = param->mNext;
|
|
prevParam = prevParam->mNext;
|
|
}
|
|
}
|
|
|
|
// Prefer virtual - this helps us bind to a virtual override instead of the concrete method implementation so we can ignore it and
|
|
// continue searching through the base types to find the original virtual method
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
SET_BETTER_OR_WORSE(newMethodInstance->mVirtual, prevMethodInstance->mVirtual);
|
|
}
|
|
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
SET_BETTER_OR_WORSE(newMethodInstance->mBlock.mLowPC != 0, prevMethodInstance->mBlock.mLowPC != 0);
|
|
}
|
|
|
|
if ((!isBetter) && (!isWorse))
|
|
{
|
|
if ((newMethodInstance->mHasThis) && (prevMethodInstance->mHasThis))
|
|
{
|
|
SET_BETTER_OR_WORSE(
|
|
newMethodInstance->mParams.mHead->mType->IsConst() == mTargetIsConst,
|
|
prevMethodInstance->mParams.mHead->mType->IsConst() == mTargetIsConst);
|
|
}
|
|
}
|
|
|
|
*outNewIsBetter = isBetter;
|
|
*outNewIsWorse = isWorse;
|
|
}
|
|
|
|
bool DwMethodMatcher::CheckMethod(DbgType* typeInstance, DbgSubprogram* checkMethod)
|
|
{
|
|
bool hadMatch = false;
|
|
|
|
checkMethod->PopulateSubprogram();
|
|
|
|
// Never consider overrides - they only get found at original method declaration
|
|
/*if ((checkMethod->mVirtual) && (checkMethod->mVTableLoc == -1))
|
|
return true;*/
|
|
|
|
//BfMethodInstance* methodInstance = mModule->GetRawMethodInstanceAtIdx(typeInstance, checkMethod->mIdx);
|
|
DbgSubprogram* methodInstance = checkMethod;
|
|
|
|
DwAutoComplete* autoComplete = mExprEvaluator->mAutoComplete;
|
|
if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo))
|
|
{
|
|
DwAutoComplete::MethodMatchEntry methodMatchEntry;
|
|
methodMatchEntry.mDwSubprogram = methodInstance;
|
|
autoComplete->mMethodMatchInfo->mInstanceList.push_back(methodMatchEntry);
|
|
}
|
|
|
|
/*if ((mHadExplicitGenericArguments) && (checkMethod->mGenericParams.size() != mBestMethodGenericArguments.size()))
|
|
goto NoMatch;*/
|
|
|
|
for (auto& checkGenericArgRef : mCheckMethodGenericArguments)
|
|
checkGenericArgRef = NULL;
|
|
|
|
/*mCheckMethodGenericArguments.resize(checkMethod->mGenericParams.size());
|
|
for (auto& genericArgRef : mCheckMethodGenericArguments)
|
|
genericArgRef = NULL;*/
|
|
|
|
int argIdx = 0;
|
|
int paramIdx = 0;
|
|
DbgType* paramsElementType = NULL;
|
|
|
|
//bool needInferGenericParams = (checkMethod->mGenericParams.size() != 0) && (!mHadExplicitGenericArguments);
|
|
|
|
DwTypeVector* genericArgumentsSubstitute = NULL;
|
|
/*if (mHadExplicitGenericArguments)
|
|
genericArgumentsSubstitute = &mBestMethodGenericArguments;
|
|
else if (needInferGenericParams)
|
|
genericArgumentsSubstitute = &mCheckMethodGenericArguments;
|
|
|
|
if (needInferGenericParams)
|
|
{
|
|
for (int argIdx = 0; argIdx < (int)mArguments.size(); argIdx++)
|
|
{
|
|
if (argIdx >= (int)checkMethod->mParams.size())
|
|
break;
|
|
auto wantType = methodInstance->mParamTypes[argIdx];
|
|
if (wantType->IsUnspecializedType())
|
|
{
|
|
if (!InferGenericArgument(mArguments[argIdx].mType, wantType))
|
|
goto NoMatch;
|
|
}
|
|
}
|
|
|
|
for (int checkArgIdx = 0; checkArgIdx < (int) checkMethod->mGenericParams.size(); checkArgIdx++)
|
|
if (mCheckMethodGenericArguments[checkArgIdx] == NULL)
|
|
goto NoMatch;
|
|
}*/
|
|
|
|
// Iterate through params
|
|
|
|
int paramOffset = checkMethod->mHasThis ? 1 : 0;
|
|
|
|
while (true)
|
|
{
|
|
// Too many arguments
|
|
if (paramIdx >= checkMethod->mParams.Size() - paramOffset)
|
|
{
|
|
break;
|
|
}
|
|
|
|
DbgVariable* paramDef = checkMethod->mParams[paramIdx + paramOffset];
|
|
|
|
//TODO:
|
|
/*if ((paramDef->mParamType == BfParamType_Params) && (paramsElementType == NULL))
|
|
{
|
|
if (paramIdx >= (int) mArguments.size())
|
|
break; // No params
|
|
|
|
if (!mArguments[argIdx])
|
|
goto NoMatch;
|
|
|
|
// Check to see if we're directly passing the params type (like an int[])
|
|
auto paramsArrayType = methodInstance->mParamTypes[paramIdx];
|
|
if (mModule->CanCast(mArguments[argIdx], paramsArrayType))
|
|
{
|
|
argIdx++;
|
|
paramIdx++;
|
|
break;
|
|
}
|
|
|
|
BF_ASSERT(paramsArrayType->IsArray());
|
|
auto arrayType = (BfArrayType*)paramsArrayType;
|
|
paramsElementType = arrayType->mTypeGenericArguments[0];
|
|
|
|
while (argIdx < (int)mArguments.size())
|
|
{
|
|
if (!mArguments[argIdx])
|
|
goto NoMatch;
|
|
if (!mModule->CanCast(mArguments[argIdx], paramsElementType))
|
|
goto NoMatch;
|
|
argIdx++;
|
|
}
|
|
break;
|
|
}*/
|
|
|
|
if (paramIdx >= (int) mArguments.size())
|
|
{
|
|
// We have defaults the rest of the way, so that's cool
|
|
//TODO:
|
|
/*if (paramDef->mParamDeclaration->mInitializer != NULL)
|
|
break;*/
|
|
|
|
// We have unused params left over
|
|
goto NoMatch;
|
|
}
|
|
|
|
auto wantType = paramDef->mType;
|
|
/*if ((genericArgumentsSubstitute != NULL) && (wantType->IsUnspecializedType()))
|
|
wantType = mModule->SpecializeMethodType(wantType, *genericArgumentsSubstitute);*/
|
|
|
|
if (!mArguments[argIdx])
|
|
goto NoMatch;
|
|
|
|
|
|
if (!mExprEvaluator->CanCast(mArguments[argIdx], wantType))
|
|
goto NoMatch;
|
|
|
|
paramIdx++;
|
|
argIdx++;
|
|
|
|
if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo))
|
|
{
|
|
auto methodMatchInfo = autoComplete->mMethodMatchInfo;
|
|
if (!methodMatchInfo->mHadExactMatch)
|
|
{
|
|
bool isBetter = false;
|
|
bool isWorse = false;
|
|
int methodIdx = (int)methodMatchInfo->mInstanceList.size() - 1;
|
|
|
|
if (methodMatchInfo->mBestIdx < (int)methodMatchInfo->mInstanceList.size())
|
|
{
|
|
auto prevMethodMatchEntry = &methodMatchInfo->mInstanceList[methodMatchInfo->mBestIdx];
|
|
if (checkMethod->mParams.Size() < (int)mArguments.size())
|
|
{
|
|
isWorse = true;
|
|
}
|
|
else if (prevMethodMatchEntry->mDwSubprogram->mParams.Size() < (int) mArguments.size())
|
|
{
|
|
isBetter = true;
|
|
}
|
|
else
|
|
{
|
|
//BfMethodInstance* prevMethodInstance = mModule->GetRawMethodInstanceAtIdx(prevMethodMatchEntry->mTypeInstance, prevMethodMatchEntry->mMethodDef->mIdx);
|
|
DbgSubprogram* prevMethodInstance = prevMethodMatchEntry->mDwSubprogram;
|
|
CompareMethods(prevMethodInstance, &prevMethodMatchEntry->mDwGenericArguments,
|
|
methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, true);
|
|
}
|
|
}
|
|
|
|
if ((argIdx > methodMatchInfo->mMostParamsMatched) ||
|
|
((argIdx == methodMatchInfo->mMostParamsMatched) && (isBetter)) ||
|
|
((argIdx == methodMatchInfo->mMostParamsMatched) && (methodIdx == methodMatchInfo->mPrevBestIdx) && (!isWorse)))
|
|
{
|
|
methodMatchInfo->mBestIdx = methodIdx;
|
|
methodMatchInfo->mMostParamsMatched = argIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: Does this ever get hit?
|
|
// Not enough arguments?
|
|
if (argIdx < (int)mArguments.size())
|
|
{
|
|
goto NoMatch;
|
|
}
|
|
|
|
// Method is applicable, check to see which method is better
|
|
if (mBestMethodDef != NULL)
|
|
{
|
|
bool isBetter = false;
|
|
bool isWorse = false;
|
|
//BfMethodInstance* prevMethodInstance = mModule->GetRawMethodInstanceAtIdx(mBestMethodTypeInstance, mBestMethodDef->mIdx);
|
|
DbgSubprogram* prevMethodInstance = mBestMethodDef;
|
|
CompareMethods(prevMethodInstance, &mBestMethodGenericArguments, methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, false);
|
|
|
|
// If we had both a 'better' and 'worse', that's ambiguous because the methods are each better in different ways (not allowed)
|
|
// And if neither better nor worse then they are equally good, which is not allowed either
|
|
if (((!isBetter) && (!isWorse)) || ((isBetter) && (isWorse)))
|
|
{
|
|
// When we have [Checked] and [Unchecked] both, they will confict here but be equal
|
|
if (!prevMethodInstance->Equals(methodInstance))
|
|
{
|
|
mExprEvaluator->Fail(StrFormat("Ambiguous method call between '%s' and '%s'", prevMethodInstance->ToString().c_str(),
|
|
methodInstance->ToString().c_str()), mTargetSrc);
|
|
}
|
|
}
|
|
|
|
if (!isBetter)
|
|
goto Done;
|
|
}
|
|
|
|
if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo))
|
|
{
|
|
auto methodMatchInfo = autoComplete->mMethodMatchInfo;
|
|
// Try to persist with previous partial match, if we have one - this keeps us from locking onto
|
|
// an incorrect method just because it had the current number of params that we've typed so far
|
|
if (methodMatchInfo->mPrevBestIdx == -1)
|
|
{
|
|
methodMatchInfo->mHadExactMatch = true;
|
|
methodMatchInfo->mBestIdx = (int) methodMatchInfo->mInstanceList.size() - 1;
|
|
}
|
|
}
|
|
|
|
hadMatch = true;
|
|
mBestMethodDef = checkMethod;
|
|
|
|
NoMatch:
|
|
if (!hadMatch)
|
|
{
|
|
if (mBestMethodDef != NULL)
|
|
return true;
|
|
|
|
//TODO:
|
|
/*if ((mHadExplicitGenericArguments) && (mBestMethodGenericArguments.size() != checkMethod->mGenericParams.size()))
|
|
return true;*/
|
|
|
|
// At least prefer a backup method that we have an address for
|
|
if ((mBackupMethodDef != NULL) && (mBackupMethodDef->mBlock.mLowPC != 0))
|
|
return true;
|
|
|
|
mBackupMethodDef = checkMethod;
|
|
// Lie temporarily to store at least one candidate (but mBestMethodDef is still NULL)
|
|
hadMatch = true;
|
|
}
|
|
|
|
if (hadMatch)
|
|
{
|
|
mBestMethodTypeInstance = typeInstance;
|
|
if (!mHadExplicitGenericArguments)
|
|
{
|
|
mBestMethodGenericArguments = mCheckMethodGenericArguments;
|
|
}
|
|
}
|
|
|
|
Done:
|
|
if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (genericArgumentsSubstitute != NULL))
|
|
{
|
|
auto methodMatchInfo = autoComplete->mMethodMatchInfo;
|
|
methodMatchInfo->mInstanceList[methodMatchInfo->mInstanceList.size() - 1].mDwGenericArguments = *genericArgumentsSubstitute;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DwMethodMatcher::CheckType(DbgType* typeInstance, bool isFailurePass)
|
|
{
|
|
typeInstance = typeInstance->RemoveModifiers();
|
|
if (typeInstance->IsPointer())
|
|
typeInstance = typeInstance->mTypeParam;
|
|
typeInstance = typeInstance->GetPrimaryType();
|
|
|
|
//bool allowPrivate = typeInstance == mExprEvaluator->GetCurrentType();
|
|
//bool allowProtected = allowPrivate || mExprEvaluator->TypeIsSubTypeOf(mExprEvaluator->GetCurrentType(), typeInstance);
|
|
bool allowPrivate = true;
|
|
bool allowProtected = true;
|
|
|
|
auto curType = typeInstance;
|
|
|
|
bool wantCtor = false;
|
|
|
|
auto baseItr = typeInstance->mBaseTypes.begin();
|
|
auto altItr = typeInstance->mAlternates.begin();
|
|
|
|
while (true)
|
|
{
|
|
bool foundMethodInType = false;
|
|
|
|
curType->PopulateType();
|
|
// Why did we have this "&&" check on there?
|
|
// It made it so invocations would fail unless we had autocompleted the type
|
|
if ((curType->mNeedsGlobalsPopulated)
|
|
/*&& ((curType->IsNamespace()) || (curType->IsRoot()))*/)
|
|
{
|
|
// These aren't proper TPI types so we don't have any method declarations until we PopulateTypeGlobals
|
|
mExprEvaluator->mDbgModule->PopulateTypeGlobals(curType);
|
|
}
|
|
|
|
for (auto methodNameEntry : curType->mMethodNameList)
|
|
{
|
|
if ((methodNameEntry->mCompileUnitId != -1) && (methodNameEntry->mName == mMethodName))
|
|
{
|
|
// If we hot-replaced this type then we replaced and parsed all the methods too
|
|
if (!curType->mCompileUnit->mDbgModule->IsObjectFile())
|
|
curType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId);
|
|
methodNameEntry->mCompileUnitId = -1;
|
|
}
|
|
}
|
|
|
|
auto checkMethod = curType->mMethodList.mHead;
|
|
while (checkMethod != NULL)
|
|
{
|
|
/*if ((!checkMethod->mHasThis) && (!checkStatic))
|
|
continue;
|
|
if ((checkMethod->mHasThis) && (!checkNonStatic))
|
|
continue;*/
|
|
|
|
// These can only be invoked when the target itself is the interface
|
|
/*if (checkMethod->mExplicitInterface != NULL)
|
|
continue;*/
|
|
|
|
if ((wantCtor) && (checkMethod->mMethodType != DbgMethodType_Ctor))
|
|
{
|
|
checkMethod = checkMethod->mNext;
|
|
continue;
|
|
}
|
|
if (!wantCtor)
|
|
{
|
|
if (checkMethod->mMethodType != DbgMethodType_Normal)
|
|
{
|
|
checkMethod = checkMethod->mNext;
|
|
continue;
|
|
}
|
|
if ((checkMethod->mName == NULL) || (checkMethod->mName != mMethodName))
|
|
{
|
|
checkMethod = checkMethod->mNext;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((!isFailurePass) && (!CheckProtection(checkMethod->mProtection, allowProtected, allowPrivate)))
|
|
{
|
|
checkMethod = checkMethod->mNext;
|
|
continue;
|
|
}
|
|
|
|
if (!curType->mHasGlobalsPopulated)
|
|
mExprEvaluator->mDbgModule->PopulateTypeGlobals(curType);
|
|
|
|
/*if (!foundMethodInType)
|
|
{
|
|
if (curType->mCompileUnit->mLanguage != DbgLanguage_Beef)
|
|
{
|
|
String fullMethodName = String(curType->mTypeName) + "::" + mMethodName;
|
|
curType->mCompileUnit->mDbgModule->EnsureMethodMapped(fullMethodName.c_str());
|
|
}
|
|
foundMethodInType = true;
|
|
}*/
|
|
|
|
if (!CheckMethod(curType, checkMethod))
|
|
return false;
|
|
|
|
checkMethod = checkMethod->mNext;
|
|
}
|
|
|
|
if (mBestMethodDef != NULL)
|
|
{
|
|
if ((mBestMethodDef->mVirtual) && (mBestMethodDef->mVTableLoc == -1))
|
|
{
|
|
// It's an override, keep searching through the base types to find the original method declaration
|
|
mBestMethodDef = NULL;
|
|
mBackupMethodDef = NULL;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
/*if (baseItr != typeInstance->mBaseTypes.end())
|
|
{
|
|
auto baseTypeEntry = *baseItr;
|
|
curType = baseTypeEntry->mBaseType;
|
|
baseItr++;
|
|
continue;;
|
|
}*/
|
|
|
|
if (altItr != typeInstance->mAlternates.end())
|
|
{
|
|
curType = *altItr;
|
|
++altItr;
|
|
continue;
|
|
}
|
|
|
|
/*allowPrivate = false;
|
|
|
|
if ((isFailurePass) && (mBackupMethodDef != NULL))
|
|
break;*/
|
|
break;
|
|
}
|
|
|
|
if (mBestMethodDef == NULL)
|
|
{
|
|
// FAILED, but select the first method which will fire an actual error on param type matching
|
|
mBestMethodDef = mBackupMethodDef;
|
|
|
|
for (auto subType : typeInstance->mSubTypeList)
|
|
{
|
|
if ((subType->mName == NULL) || (subType->IsGlobalsContainer()))
|
|
{
|
|
if (!CheckType(subType, isFailurePass))
|
|
return false;
|
|
if (mBestMethodDef != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mBestMethodDef == NULL)
|
|
{
|
|
for (auto baseTypeEntry : typeInstance->mBaseTypes)
|
|
{
|
|
if (!CheckType(baseTypeEntry->mBaseType, isFailurePass))
|
|
return false;
|
|
if (mBestMethodDef != NULL)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
DbgExprEvaluator::DbgExprEvaluator(WinDebugger* winDebugger, DbgModule* dbgModule, BfPassInstance* passInstance, int callStackIdx, int cursorPos)
|
|
{
|
|
mCountResultOverride = -1;
|
|
mCapturingChildRef = true;
|
|
mPassInstance = passInstance;
|
|
mDebugger = winDebugger;
|
|
mLanguage = DbgLanguage_NotSet;
|
|
mOrigDbgModule = dbgModule;
|
|
if (dbgModule != NULL)
|
|
{
|
|
mDebugTarget = dbgModule->mDebugTarget;
|
|
mDbgModule = dbgModule->GetLinkedModule();
|
|
}
|
|
else
|
|
{
|
|
mDebugTarget = NULL;
|
|
mDbgModule = NULL;
|
|
}
|
|
|
|
mDbgCompileUnit = NULL;
|
|
mExplicitThisExpr = NULL;
|
|
mExpectingType = NULL;
|
|
mCurMethod = NULL;
|
|
mIgnoreErrors = false;
|
|
mCallStackIdx = callStackIdx;
|
|
mCursorPos = cursorPos;
|
|
mAutoComplete = NULL;
|
|
mIsEmptyTarget = (dbgModule == NULL) || (dbgModule->mDebugTarget->mIsEmpty);
|
|
mExpressionFlags = DwEvalExpressionFlag_None;
|
|
mHadSideEffects = false;
|
|
mBlockedSideEffects = false;
|
|
mReferenceId = NULL;
|
|
mIsComplexExpression = false;
|
|
mHadMemberReference = false;
|
|
mCreatedPendingCall = false;
|
|
mValidateOnly = false;
|
|
mReceivingValue = NULL;
|
|
mCallResults = NULL;
|
|
mCallResultIdx = 0;
|
|
mCallStackPreservePos = 0;
|
|
mPropGet = NULL;
|
|
mPropSet = NULL;
|
|
mPropSrc = NULL;
|
|
}
|
|
|
|
DbgExprEvaluator::~DbgExprEvaluator()
|
|
{
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetInt(int value)
|
|
{
|
|
DbgTypedValue dbgValue;
|
|
dbgValue.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
dbgValue.mInt32 = value;
|
|
return dbgValue;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetString(const StringImpl& str)
|
|
{
|
|
String* resultPtr;
|
|
mDebugger->mLiteralSet.TryAdd(str, &resultPtr);
|
|
|
|
DbgTypedValue dbgValue;
|
|
|
|
auto language = GetLanguage();
|
|
auto charPtrType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar : DbgType_SChar, language);
|
|
if (charPtrType != NULL)
|
|
charPtrType = mDbgModule->GetPointerType(charPtrType);
|
|
if (charPtrType != NULL)
|
|
{
|
|
dbgValue.mType = charPtrType;
|
|
dbgValue.mLocalPtr = resultPtr->c_str();
|
|
dbgValue.mIsLiteral = true;
|
|
dbgValue.mDataLen = resultPtr->mLength;
|
|
}
|
|
|
|
return dbgValue;
|
|
}
|
|
|
|
void DbgExprEvaluator::Fail(const StringImpl& error, BfAstNode* node)
|
|
{
|
|
if ((!mIgnoreErrors) && (mPassInstance != NULL))
|
|
mPassInstance->Fail(error, node);
|
|
}
|
|
|
|
void DbgExprEvaluator::Warn(const StringImpl& error, BfAstNode * node)
|
|
{
|
|
if ((!mIgnoreErrors) && (mPassInstance != NULL))
|
|
mPassInstance->Warn(0, error, node);
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::GetExpectingType()
|
|
{
|
|
if ((mExpectingType == NULL) && (!mExpectingTypeName.empty()))
|
|
{
|
|
mExpectingType = ResolveTypeRef(mExpectingTypeName);
|
|
mExpectingTypeName.clear();
|
|
}
|
|
return mExpectingType;
|
|
}
|
|
|
|
void DbgExprEvaluator::GetNamespaceSearch()
|
|
{
|
|
if (!mNamespaceSearch.empty())
|
|
return;
|
|
|
|
auto currentMethod = GetCurrentMethod();
|
|
if (currentMethod != NULL)
|
|
{
|
|
auto parent = currentMethod->GetParent();
|
|
while (parent != NULL)
|
|
{
|
|
parent = parent->GetPrimaryType();
|
|
mNamespaceSearch.push_back(parent);
|
|
parent = parent->mParent;
|
|
}
|
|
}
|
|
|
|
if (!mNamespaceSearchStr.empty())
|
|
{
|
|
auto language = GetLanguage();
|
|
int lastComma = -1;
|
|
for (int i = 0; i < (int)mNamespaceSearchStr.length(); i++)
|
|
{
|
|
if (mNamespaceSearchStr[i] == ',')
|
|
{
|
|
String namespaceEntry = mNamespaceSearchStr.Substring(lastComma + 1, i - lastComma - 1);
|
|
auto dbgTypeEntry = mDbgModule->mTypeMap.Find(namespaceEntry.c_str(), language);
|
|
if (dbgTypeEntry != NULL)
|
|
mNamespaceSearch.push_back(dbgTypeEntry->mValue);
|
|
lastComma = i;
|
|
}
|
|
}
|
|
|
|
String namespaceEntry = mNamespaceSearchStr.Substring(lastComma + 1);
|
|
auto dbgTypeEntry = mDbgModule->mTypeMap.Find(namespaceEntry.c_str(), language);
|
|
if (dbgTypeEntry != NULL)
|
|
mNamespaceSearch.push_back(dbgTypeEntry->mValue);
|
|
}
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::ResolveSubTypeRef(DbgType* checkType, const StringImpl& name)
|
|
{
|
|
checkType->PopulateType();
|
|
|
|
for (auto subType : checkType->mSubTypeList)
|
|
{
|
|
if (strcmp(subType->mTypeName, name.c_str()) == 0)
|
|
return subType;
|
|
}
|
|
|
|
for (auto checkBaseType : checkType->mBaseTypes)
|
|
{
|
|
DbgType* subType = ResolveSubTypeRef(checkBaseType->mBaseType, name);
|
|
if (subType != NULL)
|
|
return subType;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::FixType(DbgType* dbgType)
|
|
{
|
|
if (dbgType->IsBfObject())
|
|
dbgType = mDbgModule->GetPointerType(dbgType);
|
|
return dbgType;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::FixThis(const DbgTypedValue& thisVal)
|
|
{
|
|
/*if ((thisVal.mType != NULL) && (thisVal.mType->GetLanguage() == DbgLanguage_Beef) &&
|
|
(!thisVal.mType->IsBfObjectPtr()) && (thisVal.mType->IsPointer()))
|
|
{
|
|
// Beef structs expect 'this' as a valuetype, not a pointer
|
|
DbgTypedValue fixedThisVal;
|
|
fixedThisVal.mType = thisVal.mType->mTypeParam;
|
|
fixedThisVal.mSrcAddress = thisVal.mPtr;
|
|
fixedThisVal.mPtr = thisVal.mPtr;
|
|
int byteCount = fixedThisVal.mType->GetByteCount();
|
|
if (byteCount <= 8) // For typed primitive loads
|
|
mDebugger->ReadMemory(fixedThisVal.mSrcAddress, byteCount, &fixedThisVal.mInt64);
|
|
return fixedThisVal;
|
|
}*/
|
|
return thisVal;
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::ResolveTypeRef(BfTypeReference* typeRef)
|
|
{
|
|
mDbgModule->ParseTypeData();
|
|
|
|
if (auto ptrTypeRef = BfNodeDynCastExact<BfPointerTypeRef>(typeRef))
|
|
{
|
|
// If it's a pointer type ref then create our own type
|
|
auto innerType = ResolveTypeRef(ptrTypeRef->mElementType);
|
|
if (innerType == NULL)
|
|
return NULL;
|
|
return mDbgModule->GetPointerType(innerType);
|
|
}
|
|
|
|
if (auto arrayTypeRef = BfNodeDynCastExact<BfArrayTypeRef>(typeRef))
|
|
{
|
|
if (arrayTypeRef->mParams.size() == 1)
|
|
{
|
|
if (auto literalExpr = BfNodeDynCastExact<BfLiteralExpression>(arrayTypeRef->mParams[0]))
|
|
{
|
|
auto elementType = ResolveTypeRef(arrayTypeRef->mElementType);
|
|
if (elementType == NULL)
|
|
return NULL;
|
|
|
|
int count = 1;
|
|
if ((literalExpr->mValue.mTypeCode >= BfTypeCode_Int16) && (literalExpr->mValue.mTypeCode <= BfTypeCode_UIntUnknown))
|
|
{
|
|
count = literalExpr->mValue.mInt32;
|
|
if (count < 0)
|
|
{
|
|
Fail(StrFormat("Illegal array size"), arrayTypeRef->mParams[0]);
|
|
count = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Illegal array size type"), arrayTypeRef->mParams[0]);
|
|
}
|
|
|
|
return mDbgModule->GetSizedArrayType(elementType, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto declTypeRef = BfNodeDynCastExact<BfExprModTypeRef>(typeRef))
|
|
{
|
|
mResult = DbgTypedValue();
|
|
VisitChild(declTypeRef->mTarget);
|
|
auto type = mResult.mType;
|
|
mResult = DbgTypedValue();
|
|
return type;
|
|
}
|
|
|
|
String name = typeRef->ToString();
|
|
if (name.StartsWith("_T_"))
|
|
{
|
|
int idx = atoi(name.c_str() + 3);
|
|
if ((idx >= 0) && (idx < (int)mDbgModule->mTypes.size()))
|
|
return mDbgModule->mTypes[idx];
|
|
}
|
|
|
|
auto entry = mDbgModule->mTypeMap.Find(name.c_str(), GetLanguage());
|
|
if (entry != NULL)
|
|
return FixType(entry->mValue);
|
|
|
|
if (mExplicitThis)
|
|
{
|
|
bool isPtr = typeRef->IsA<BfPointerTypeRef>();
|
|
if (isPtr)
|
|
{
|
|
BfPointerTypeRef* ptrTypeRef = (BfPointerTypeRef*)typeRef;
|
|
BfTypeReference* innerTypeRef = ptrTypeRef->mElementType;
|
|
name = innerTypeRef->ToString();
|
|
}
|
|
|
|
auto checkType = mExplicitThis.mType;
|
|
if (checkType->IsPointer())
|
|
checkType = checkType->mTypeParam;
|
|
|
|
ResolveSubTypeRef(checkType, name);
|
|
}
|
|
|
|
auto currentType = GetCurrentType();
|
|
if (currentType != NULL)
|
|
{
|
|
GetNamespaceSearch();
|
|
for (auto usingNamespace : mNamespaceSearch)
|
|
{
|
|
auto usingNamespaceString = usingNamespace->ToString();
|
|
entry = mDbgModule->mTypeMap.Find((usingNamespaceString + "." + name).c_str(), GetLanguage());
|
|
if (entry != NULL)
|
|
return FixType(entry->mValue);
|
|
}
|
|
}
|
|
|
|
Fail(StrFormat("Unable to locate type"), typeRef);
|
|
return NULL;
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::ResolveTypeRef(BfAstNode* typeRef, BfAstNode** parentChildRef)
|
|
{
|
|
StringT<128> name = typeRef->ToString();
|
|
if ((name.StartsWith("_T_")) && ((int)name.IndexOf('.') == -1))
|
|
{
|
|
int endIdx = name.length();
|
|
for (int i = 3; i < (int)name.length(); i++)
|
|
{
|
|
char c = name[i];
|
|
if ((c < '0') || (c > '9'))
|
|
{
|
|
endIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int idx = atoi(name.c_str() + 3);
|
|
if ((idx >= 0) && (idx < (int)mDbgModule->mTypes.size()))
|
|
{
|
|
if ((mExplicitThisExpr != NULL) && (parentChildRef != NULL))
|
|
mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(typeRef, parentChildRef, true));
|
|
DbgType* dbgType = mDbgModule->mTypes[idx];
|
|
for (int i = endIdx; i < (int)name.length(); i++)
|
|
{
|
|
if (name[i] == '*')
|
|
dbgType = mDbgModule->GetPointerType(dbgType);
|
|
}
|
|
return dbgType;
|
|
}
|
|
}
|
|
|
|
/*for (int i = 1; i < (int)name.length(); i++)
|
|
{
|
|
if ((name[i] == ':') && (name[i - 1] == ':'))
|
|
{
|
|
name[i] = '.';
|
|
name.erase(name.begin() + i - 1);
|
|
i--;
|
|
}
|
|
}*/
|
|
|
|
auto language = GetLanguage();
|
|
auto entry = mDbgModule->FindType(name.c_str(), language);
|
|
if (entry != NULL)
|
|
return entry->mValue;
|
|
auto currentType = GetCurrentType();
|
|
if (currentType != NULL)
|
|
{
|
|
GetNamespaceSearch();
|
|
for (auto usingNamespace : mNamespaceSearch)
|
|
{
|
|
auto usingNamespaceString = usingNamespace->ToString();
|
|
entry = mDbgModule->FindType((usingNamespaceString + "." + name).c_str(), language);
|
|
if (entry != NULL)
|
|
return entry->mValue;
|
|
}
|
|
}
|
|
Fail("Unable to locate type", typeRef);
|
|
return NULL;
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::ResolveTypeRef(const StringImpl& typeName)
|
|
{
|
|
auto entry = mDbgModule->mTypeMap.Find(typeName.c_str(), GetLanguage());
|
|
if (entry != NULL)
|
|
return entry->mValue;
|
|
mPassInstance->Fail("Unable to locate type: " + typeName);
|
|
return NULL;
|
|
}
|
|
|
|
bool DbgExprEvaluator::TypeIsSubTypeOf(DbgType* srcType, DbgType* wantType, int* thisOffset, addr_target* thisAddr)
|
|
{
|
|
if (srcType == NULL)
|
|
return false;
|
|
|
|
srcType = srcType->GetPrimaryType();
|
|
wantType = wantType->GetPrimaryType();
|
|
|
|
if ((wantType->IsPrimitiveType()) && (srcType->IsPrimitiveType()))
|
|
{
|
|
return wantType->mTypeCode == srcType->mTypeCode;
|
|
}
|
|
|
|
if ((srcType == NULL) || (wantType == NULL))
|
|
return false;
|
|
if (srcType->Equals(wantType))
|
|
return true;
|
|
|
|
if (srcType->mTypeCode == DbgType_TypeDef)
|
|
return TypeIsSubTypeOf(srcType->mTypeParam, wantType);
|
|
if (wantType->mTypeCode == DbgType_TypeDef)
|
|
return TypeIsSubTypeOf(srcType, wantType->mTypeParam);
|
|
|
|
srcType->PopulateType();
|
|
for (auto srcBaseType : srcType->mBaseTypes)
|
|
{
|
|
bool isSubtype = TypeIsSubTypeOf(srcBaseType->mBaseType, wantType);
|
|
if (isSubtype)
|
|
{
|
|
if (srcBaseType->mVTableOffset != -1)
|
|
{
|
|
if (thisAddr == NULL)
|
|
return false;
|
|
int32 virtThisOffset = 0;
|
|
|
|
addr_target vtableAddr = 0;
|
|
gDebugger->ReadMemory(*thisAddr, sizeof(addr_target), &vtableAddr);
|
|
gDebugger->ReadMemory(vtableAddr + srcBaseType->mVTableOffset * sizeof(int32), sizeof(int32), &virtThisOffset);
|
|
|
|
*thisOffset += virtThisOffset;
|
|
}
|
|
else if (thisOffset != NULL)
|
|
*thisOffset += srcBaseType->mThisOffset;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetBeefTypeById(int typeId)
|
|
{
|
|
if (mDebugTarget->mTargetBinary == NULL)
|
|
return DbgTypedValue();
|
|
|
|
if (mDebugTarget->mTargetBinary->mBfTypesInfoAddr == 0)
|
|
{
|
|
mDebugTarget->mTargetBinary->mBfTypesInfoAddr = -1;
|
|
|
|
mDebugTarget->mTargetBinary->ParseTypeData();
|
|
auto typeTypeEntry = mDebugTarget->mTargetBinary->FindType("System.Type", DbgLanguage_Beef);
|
|
if ((typeTypeEntry != NULL) && (typeTypeEntry->mValue != NULL))
|
|
{
|
|
auto typeType = typeTypeEntry->mValue;
|
|
mDebugTarget->mTargetBinary->mBfTypeType = typeType;
|
|
if (typeType->mNeedsGlobalsPopulated)
|
|
typeType->mCompileUnit->mDbgModule->PopulateTypeGlobals(typeType);
|
|
|
|
for (auto member : typeType->mMemberList)
|
|
{
|
|
if ((member->mIsStatic) && (member->mName != NULL) && (strcmp(member->mName, "sTypes") == 0) && (member->mLocationData != NULL))
|
|
{
|
|
auto stackFrame = GetStackFrame();
|
|
DbgAddrType addrType;
|
|
mDebugTarget->mTargetBinary->mBfTypesInfoAddr = member->mCompileUnit->mDbgModule->EvaluateLocation(NULL, member->mLocationData, member->mLocationLen, stackFrame, &addrType);
|
|
}
|
|
}
|
|
|
|
if (mDebugTarget->mTargetBinary->mBfTypesInfoAddr <= 0)
|
|
{
|
|
mDebugTarget->mTargetBinary->ParseSymbolData();
|
|
auto entry = mDebugTarget->mTargetBinary->mSymbolNameMap.Find(
|
|
#ifdef BF_DBG_64
|
|
"?sTypes@Type@System@bf@@2PEAPEAV123@A"
|
|
#else
|
|
"?sTypes@Type@System@bf@@2PAPAV123@A"
|
|
#endif
|
|
);
|
|
|
|
if (entry)
|
|
mDebugTarget->mTargetBinary->mBfTypesInfoAddr = entry->mValue->mAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mDebugTarget->mTargetBinary->mBfTypesInfoAddr > 0)
|
|
{
|
|
DbgTypedValue typedVal;
|
|
typedVal.mType = mDebugTarget->mTargetBinary->mBfTypeType;
|
|
addr_target addr = mDebugTarget->mTargetBinary->mBfTypesInfoAddr + typeId * sizeof(addr_target);
|
|
typedVal.mSrcAddress = mDebugger->ReadMemory<addr_target>(addr);
|
|
return typedVal;
|
|
}
|
|
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
void DbgExprEvaluator::BeefStringToString(addr_target addr, String& outStr)
|
|
{
|
|
mDebugTarget->GetCompilerSettings();
|
|
int objectSize = mDebugTarget->mBfObjectSize;
|
|
|
|
int32 strLen = 0;
|
|
addr_target charPtr = 0;
|
|
if (!mDebugTarget->mBfHasLargeStrings)
|
|
{
|
|
strLen = mDebugger->ReadMemory<int32>(addr + objectSize);
|
|
int32 allocSizeAndFlags = mDebugger->ReadMemory<int32>(addr + objectSize + 4);
|
|
if ((allocSizeAndFlags & 0x40000000) != 0)
|
|
charPtr = mDebugger->ReadMemory<addr_target>(addr + objectSize + 4 + 4);
|
|
else
|
|
charPtr = addr + objectSize + 4 + 4;
|
|
}
|
|
else
|
|
{
|
|
strLen = mDebugger->ReadMemory<int32>(addr + objectSize);
|
|
int64 allocSizeAndFlags = mDebugger->ReadMemory<int64>(addr + objectSize + 8);
|
|
if ((allocSizeAndFlags & 0x40000000'00000000LL) != 0)
|
|
charPtr = mDebugger->ReadMemory<addr_target>(addr + objectSize + 8 + 8);
|
|
else
|
|
charPtr = addr + objectSize + 4 + 4;
|
|
}
|
|
|
|
if ((strLen > 0) && (strLen < 4096))
|
|
{
|
|
outStr.Append('?', strLen);
|
|
mDebugger->ReadMemory(charPtr, strLen, &outStr[outStr.length() - strLen]);
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::BeefStringToString(const DbgTypedValue& val, String& outStr)
|
|
{
|
|
mDebugTarget->GetCompilerSettings();
|
|
|
|
auto useVal = val;
|
|
if (useVal.mType->IsBfObjectPtr())
|
|
{
|
|
useVal.mType = useVal.mType->mTypeParam;
|
|
useVal.mSrcAddress = useVal.mPtr;
|
|
}
|
|
|
|
BeefStringToString(useVal.mSrcAddress, outStr);
|
|
}
|
|
|
|
void DbgExprEvaluator::BeefTypeToString(const DbgTypedValue& val, String& outStr)
|
|
{
|
|
if (!val)
|
|
return;
|
|
|
|
mDebugTarget->GetCompilerSettings();
|
|
|
|
auto useVal = val;
|
|
if (useVal.mType->IsBfObjectPtr())
|
|
{
|
|
useVal.mType = useVal.mType->mTypeParam;
|
|
useVal.mSrcAddress = useVal.mPtr;
|
|
}
|
|
|
|
typedef int32 _TypeId;
|
|
typedef uint32 _TypeFlags;
|
|
typedef int8 _TypeCode;
|
|
|
|
#pragma pack(push, 1)
|
|
struct _Type
|
|
{
|
|
int32 mSize;
|
|
_TypeId mTypeId;
|
|
_TypeId mBoxedType;
|
|
_TypeFlags mTypeFlags;
|
|
int32 mMemberDataOffset;
|
|
_TypeCode mTypeCode;
|
|
uint8 mAlign;
|
|
};
|
|
|
|
struct _PointerType : _Type
|
|
{
|
|
int16 mPadding0;
|
|
_TypeCode mElementType;
|
|
};
|
|
|
|
struct _SizedArrayType : _Type
|
|
{
|
|
int16 mPadding0;
|
|
_TypeId mElementType;
|
|
int32 mElementCount;
|
|
};
|
|
|
|
struct _String
|
|
{
|
|
|
|
};
|
|
struct _MethodData
|
|
{
|
|
|
|
};
|
|
struct _FieldData
|
|
{
|
|
|
|
};
|
|
struct _ClassVData
|
|
{
|
|
|
|
};
|
|
|
|
struct _TypeInstance : public _Type
|
|
{
|
|
#ifdef BF_DBG_32
|
|
int16 mPadding0;
|
|
#else
|
|
int16 mPadding0;
|
|
#endif
|
|
|
|
addr_target mTypeClassVData;
|
|
addr_target mName;
|
|
addr_target mNamespace;
|
|
int32 mInstSize;
|
|
int32 mInstAlign;
|
|
int32 mCustomAttributesIdx;
|
|
_TypeId mBaseType;
|
|
_TypeId mUnderlyingType;
|
|
_TypeId mOuterType;
|
|
int32 mInheritanceId;
|
|
int32 mInheritanceCount;
|
|
|
|
uint8 mInterfaceSlot;
|
|
uint8 mInterfaceCount;
|
|
int16 mInterfaceMethodCount;
|
|
int16 mMethodDataCount;
|
|
int16 mPropertyDataCount;
|
|
int16 mFieldDataCount;
|
|
|
|
#ifdef BF_DBG_32
|
|
int16 mPadding1;
|
|
#else
|
|
int8 mPadding1[6];
|
|
#endif
|
|
|
|
addr_target mInterfaceDataPtr;
|
|
addr_target mInterfaceMethodTable;
|
|
addr_target mMethodDataPtr;
|
|
addr_target mPropertyDataPtr;
|
|
addr_target mFieldDataPtr;
|
|
addr_target mCustomAttrDataPtr;
|
|
};
|
|
|
|
struct _SpecializedGenericType : _TypeInstance
|
|
{
|
|
_TypeId mUnspecializedType;
|
|
#ifdef BF_DBG_64
|
|
int32 mPadding0;
|
|
#endif
|
|
addr_target mResolvedTypeRefs;
|
|
};
|
|
|
|
struct _ArrayType : _SpecializedGenericType
|
|
{
|
|
int32 mElementSize;
|
|
uint8 mRank;
|
|
uint8 mElemensDataOffset;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
int typeIdSize = sizeof(_TypeId);
|
|
int ptrSize = (int)sizeof(addr_target);
|
|
int objectSize = mDebugTarget->mBfObjectSize;
|
|
int typeSize = sizeof(_Type);
|
|
|
|
int typeInstanceSize = objectSize + sizeof(_TypeInstance);
|
|
|
|
auto addr = useVal.mSrcAddress;
|
|
auto typeAddr = addr + objectSize;
|
|
|
|
_TypeFlags typeFlags = mDebugger->ReadMemory<_TypeFlags>(typeAddr + offsetof(_Type, mTypeFlags));
|
|
|
|
if ((typeFlags & BfTypeFlags_Array) != 0)
|
|
{
|
|
_TypeId unspecializedTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_SpecializedGenericType, mUnspecializedType));
|
|
addr_target elementsArrayAddr = mDebugger->ReadMemory<addr_target>(typeAddr + offsetof(_SpecializedGenericType, mResolvedTypeRefs));
|
|
_TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(elementsArrayAddr);
|
|
|
|
auto elementType = GetBeefTypeById(elementTypeId);
|
|
BeefTypeToString(elementType, outStr);
|
|
|
|
outStr += "[";
|
|
int rank = mDebugger->ReadMemory<uint8>(typeAddr + offsetof(_ArrayType, mRank));
|
|
for (int commaIdx = 0; commaIdx < rank - 1; commaIdx++)
|
|
outStr += ",";
|
|
outStr += "]";
|
|
}
|
|
else if ((typeFlags & BfTypeFlags_Pointer) != 0)
|
|
{
|
|
_TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_PointerType, mElementType));
|
|
auto elementType = GetBeefTypeById(elementTypeId);
|
|
BeefTypeToString(elementType, outStr);
|
|
outStr += "*";
|
|
}
|
|
else if ((typeFlags & BfTypeFlags_Delegate) != 0)
|
|
{
|
|
outStr += "delegate";
|
|
}
|
|
else if ((typeFlags & BfTypeFlags_Function) != 0)
|
|
{
|
|
outStr += "function";
|
|
}
|
|
// else if ((typeFlags & BfTypeFlags_Tuple) != 0)
|
|
// {
|
|
// outStr += "function";
|
|
// }
|
|
else if ((typeFlags & BfTypeFlags_SizedArray) != 0)
|
|
{
|
|
_TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + objectSize + offsetof(_SizedArrayType, mElementType));
|
|
auto elementType = GetBeefTypeById(elementTypeId);
|
|
BeefTypeToString(elementType, outStr);
|
|
int elementCount = mDebugger->ReadMemory<int32>(typeAddr + objectSize + offsetof(_SizedArrayType, mElementCount));
|
|
outStr += StrFormat("[%d]", elementCount);
|
|
}
|
|
else if (((typeFlags & BfTypeFlags_Struct) != 0) || ((typeFlags & BfTypeFlags_TypedPrimitive) != 0) || ((typeFlags & BfTypeFlags_Object) != 0))
|
|
{
|
|
addr_target addr0 = typeAddr;
|
|
addr_target addr1 = typeAddr + offsetof(_Type, mTypeFlags);
|
|
addr_target addr2 = typeAddr + offsetof(_Type, mAlign);
|
|
addr_target addr3 = typeAddr + offsetof(_TypeInstance, mPadding0);
|
|
addr_target addr4 = typeAddr + offsetof(_TypeInstance, mTypeClassVData);
|
|
|
|
addr_target namePtr = mDebugger->ReadMemory<addr_target>(typeAddr + offsetof(_TypeInstance, mName));
|
|
addr_target namespacePtr = mDebugger->ReadMemory<addr_target>(typeAddr + offsetof(_TypeInstance, mNamespace));
|
|
int outerTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_TypeInstance, mOuterType));
|
|
DbgTypedValue outerType;
|
|
|
|
if (outerTypeId != 0)
|
|
{
|
|
outerType = GetBeefTypeById(outerTypeId);
|
|
BeefTypeToString(outerType, outStr);
|
|
outStr += ".";
|
|
}
|
|
else if (namespacePtr != 0)
|
|
{
|
|
String namespaceName;
|
|
BeefStringToString(namespacePtr, namespaceName);
|
|
if (!namespaceName.empty())
|
|
{
|
|
outStr += namespaceName;
|
|
outStr += ".";
|
|
}
|
|
}
|
|
BeefStringToString(namePtr, outStr);
|
|
|
|
if ((typeFlags & BfTypeFlags_SpecializedGeneric) != 0)
|
|
{
|
|
int unspecializedTypeId = mDebugger->ReadMemory<_TypeId>(addr + typeInstanceSize);
|
|
auto unspecializedType = GetBeefTypeById(unspecializedTypeId);
|
|
|
|
outStr += "<";
|
|
|
|
if (unspecializedType)
|
|
{
|
|
int startGenericIdx = 0;
|
|
int genericCount = mDebugger->ReadMemory<uint8>(unspecializedType.mSrcAddress + typeInstanceSize);
|
|
|
|
if (outerType)
|
|
{
|
|
int outerUnspecializedTypeId = mDebugger->ReadMemory<_TypeId>(outerType.mSrcAddress + typeInstanceSize);
|
|
auto outerUnspecializedType = GetBeefTypeById(outerUnspecializedTypeId);
|
|
startGenericIdx = mDebugger->ReadMemory<uint8>(outerUnspecializedType.mSrcAddress + typeInstanceSize);
|
|
}
|
|
|
|
addr_target genericParamsPtr = mDebugger->ReadMemory<addr_target>(addr + BF_ALIGN(typeInstanceSize + typeIdSize, ptrSize));
|
|
for (int i = startGenericIdx; i < genericCount; i++)
|
|
{
|
|
int genericParamTypeId = mDebugger->ReadMemory<_TypeId>(genericParamsPtr + i * typeIdSize);
|
|
if (i > startGenericIdx)
|
|
outStr += ", ";
|
|
BeefTypeToString(GetBeefTypeById(genericParamTypeId), outStr);
|
|
}
|
|
}
|
|
|
|
outStr += ">";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BfTypeCode typeCode = (BfTypeCode)mDebugger->ReadMemory<uint8>(typeAddr + (int)offsetof(_Type, mTypeCode));
|
|
if (typeCode == BfTypeCode_Pointer)
|
|
{
|
|
_TypeId elementType = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_PointerType, mElementType));
|
|
BeefTypeToString(GetBeefTypeById(elementType), outStr);
|
|
outStr += "*";
|
|
return;
|
|
}
|
|
|
|
switch (typeCode)
|
|
{
|
|
case BfTypeCode_None: outStr += "void"; break;
|
|
case BfTypeCode_CharPtr: outStr += "char8*"; break;
|
|
case BfTypeCode_Pointer: outStr += "*"; break;
|
|
case BfTypeCode_NullPtr: outStr += ""; break;
|
|
case BfTypeCode_Var: outStr += "var"; break;
|
|
case BfTypeCode_Let: outStr += "let"; break;
|
|
case BfTypeCode_Boolean: outStr += "bool"; break;
|
|
case BfTypeCode_Int8: outStr += "int8"; break;
|
|
case BfTypeCode_UInt8: outStr += "uint8"; break;
|
|
case BfTypeCode_Int16: outStr += "int16"; break;
|
|
case BfTypeCode_UInt16: outStr += "uint16"; break;
|
|
case BfTypeCode_Int32: outStr += "int32"; break;
|
|
case BfTypeCode_UInt32: outStr += "uint32"; break;
|
|
case BfTypeCode_Int64: outStr += "int64"; break;
|
|
case BfTypeCode_UInt64: outStr += "uint64"; break;
|
|
case BfTypeCode_IntPtr: outStr += "int"; break;
|
|
case BfTypeCode_UIntPtr: outStr += "uint"; break;
|
|
case BfTypeCode_IntUnknown: outStr += "int unknown"; break;
|
|
case BfTypeCode_UIntUnknown: outStr += "uint unknown"; break;
|
|
case BfTypeCode_Char8: outStr += "char8"; break;
|
|
case BfTypeCode_Char16: outStr += "char16"; break;
|
|
case BfTypeCode_Char32: outStr += "char32"; break;
|
|
case BfTypeCode_Float: outStr += "float"; break;
|
|
case BfTypeCode_Double: outStr += "double"; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CPUStackFrame* DbgExprEvaluator::GetStackFrame()
|
|
{
|
|
if (mIsEmptyTarget)
|
|
return NULL;
|
|
|
|
static CPUStackFrame emptyStackFrame;
|
|
if (mCallStackIdx == -1)
|
|
return &emptyStackFrame;
|
|
|
|
if (mDebugger->mRunState == RunState_NotStarted)
|
|
return &emptyStackFrame;
|
|
|
|
if (mDebugger->mCallStack.size() == 0)
|
|
mDebugger->UpdateCallStack();
|
|
if (mCallStackIdx >= (int)mDebugger->mCallStack.size())
|
|
return &emptyStackFrame;
|
|
|
|
return mDebugger->mCallStack[mCallStackIdx];
|
|
}
|
|
|
|
CPURegisters* DbgExprEvaluator::GetRegisters()
|
|
{
|
|
if (mIsEmptyTarget)
|
|
return NULL;
|
|
auto stackFrame = GetStackFrame();
|
|
if (stackFrame != NULL)
|
|
return &stackFrame->mRegisters;
|
|
return NULL;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetRegister(const StringImpl& regName)
|
|
{
|
|
if (mIsEmptyTarget)
|
|
return DbgTypedValue();
|
|
if (!mDebugger->mIsRunning)
|
|
return DbgTypedValue();
|
|
|
|
Array<RegForm>* regForms = NULL;
|
|
if (mCallStackIdx != -1)
|
|
{
|
|
if (mCallStackIdx < (int)mDebugger->mCallStack.size())
|
|
regForms = &mDebugger->mCallStack[mCallStackIdx]->mRegForms;
|
|
}
|
|
DbgTypedValue result = mDebugger->GetRegister(regName, GetLanguage(), GetRegisters(), regForms);
|
|
if ((result) && (mReferenceId != NULL))
|
|
{
|
|
int regNum = CPURegisters::GetCompositeRegister(result.mRegNum);
|
|
const char* regName = CPURegisters::GetRegisterName(regNum);
|
|
if (regName != NULL)
|
|
*mReferenceId = String("$") + regName;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DbgSubprogram* DbgExprEvaluator::GetCurrentMethod()
|
|
{
|
|
if (mIsEmptyTarget)
|
|
return NULL;
|
|
|
|
if (!mDebugger->mIsRunning)
|
|
return NULL;
|
|
|
|
if (mCallStackIdx == -1)
|
|
return NULL;
|
|
|
|
mDebugger->UpdateCallStackMethod(mCallStackIdx);
|
|
if (mCallStackIdx >= (int)mDebugger->mCallStack.size())
|
|
return NULL;
|
|
auto callStack = mDebugger->mCallStack[mCallStackIdx];
|
|
auto subProgram = callStack->mSubProgram;
|
|
if (subProgram != NULL)
|
|
subProgram->PopulateSubprogram();
|
|
return subProgram;
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::GetCurrentType()
|
|
{
|
|
auto curMethod = GetCurrentMethod();
|
|
if (curMethod != NULL)
|
|
return curMethod->mParentType;
|
|
return NULL;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetThis()
|
|
{
|
|
if (mExplicitThis)
|
|
return mExplicitThis;
|
|
|
|
String findName = "this";
|
|
CPUStackFrame* stackFrame = GetStackFrame();
|
|
if (stackFrame != NULL)
|
|
{
|
|
intptr valAddr;
|
|
DbgType* valType;
|
|
DbgAddrType addrType = DbgAddrType_Value;
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
//valType = valType->RemoveModifiers();
|
|
return FixThis(ReadTypedValue(NULL, valType, valAddr, addrType));
|
|
}
|
|
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
DbgTypedValue result = ReadTypedValue(NULL, valType, valAddr, addrType);
|
|
if (!result)
|
|
return result;
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
return FixThis(LookupField(NULL, result, "__this"));
|
|
}
|
|
}
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetDefaultTypedValue(DbgType* srcType)
|
|
{
|
|
DbgTypedValue typedValue;
|
|
typedValue.mType = srcType;
|
|
return typedValue;
|
|
}
|
|
|
|
String DbgExprEvaluator::TypeToString(DbgType* type)
|
|
{
|
|
return type->ToString();
|
|
}
|
|
|
|
bool DbgExprEvaluator::CheckHasValue(DbgTypedValue typedValue, BfAstNode* refNode)
|
|
{
|
|
if ((!typedValue) && (typedValue.mType != NULL))
|
|
{
|
|
mResult = DbgTypedValue();
|
|
Fail("Value required", refNode);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//TODO: Expand this
|
|
bool DbgExprEvaluator::CanCast(DbgTypedValue typedVal, DbgType* toType, BfCastFlags castFlags)
|
|
{
|
|
DbgType* fromType = typedVal.mType;
|
|
|
|
if (fromType == toType)
|
|
return true;
|
|
|
|
if ((fromType->IsConst()) && (!toType->IsConst()))
|
|
return false;
|
|
|
|
fromType = fromType->RemoveModifiers();
|
|
toType = toType->RemoveModifiers();
|
|
|
|
// Ptr -> const Ptr
|
|
if ((fromType->IsPointer()) && (toType->IsPointer()))
|
|
{
|
|
if (toType->mTypeParam->IsConst())
|
|
{
|
|
auto primaryFrom = fromType->mTypeParam->GetPrimaryType();
|
|
auto primaryTo = toType->mTypeParam->mTypeParam->GetPrimaryType();
|
|
if ((primaryFrom->IsCompositeType()) || (primaryTo->IsCompositeType()))
|
|
{
|
|
if (primaryFrom == primaryTo)
|
|
return true;
|
|
}
|
|
else if (primaryFrom->IsPrimitiveType())
|
|
{
|
|
return primaryFrom->mTypeCode == primaryTo->mTypeCode;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((fromType->IsCompositeType()) && ((toType->IsCompositeType() || (toType->IsInterface()))))
|
|
{
|
|
bool allowCast = false;
|
|
|
|
//TODO: Allow explicit interface casts on any object
|
|
//TODO: Insert cast checking code
|
|
|
|
int thisOffset = 0;
|
|
addr_target thisVal = typedVal.mSrcAddress;
|
|
if (TypeIsSubTypeOf(fromType, toType, &thisOffset, &thisVal))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType()))
|
|
{
|
|
DbgTypeCode fromTypeCode = fromType->mTypeCode;
|
|
DbgTypeCode toTypeCode = toType->mTypeCode;
|
|
if ((fromTypeCode == toTypeCode))
|
|
return true;
|
|
|
|
// Must be from a default int to do an implicit constant cast, not casted by user, ie: (ushort)123
|
|
if ((toType->IsInteger()) /*&& (typedVal.mValue != NULL) && (fromTypeCode == DbgType_i32)*/)
|
|
{
|
|
// Allow constant ints to be implicitly downcasted if they fit
|
|
if (typedVal.mIsLiteral)
|
|
{
|
|
int64 srcVal = typedVal.GetInt64();
|
|
if (toType->IsSigned())
|
|
{
|
|
int64 minVal = -(1LL << (8 * toType->mSize - 1));
|
|
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
return true;
|
|
}
|
|
else if (toType->mSize == 8) // ulong
|
|
{
|
|
if (srcVal > 0)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
int64 minVal = 0;
|
|
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (toTypeCode)
|
|
{
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_UChar:
|
|
case DbgType_u8:
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case DbgType_SChar16:
|
|
case DbgType_i16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_u16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_SChar32:
|
|
case DbgType_i32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_UChar32:
|
|
case DbgType_u32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
return true;
|
|
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_i64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_i64:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_u64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar32:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_Single:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar32:
|
|
return true;
|
|
}
|
|
break;
|
|
case DbgType_Double:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_i64:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return true;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
return true;
|
|
case DbgType_Single:
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::Cast(BfAstNode* srcNode, const DbgTypedValue& typedVal, DbgType* toType, bool explicitCast, bool silentFail)
|
|
{
|
|
if (!typedVal)
|
|
return typedVal;
|
|
|
|
DbgType* fromType = typedVal.mType;
|
|
fromType = fromType->RemoveModifiers();
|
|
toType = toType->RemoveModifiers();
|
|
|
|
fromType = fromType->GetPrimaryType();
|
|
toType = toType->GetPrimaryType();
|
|
|
|
if ((toType->IsBfObject()) && (fromType->IsPointer()))
|
|
toType = toType->GetDbgModule()->GetPointerType(toType);
|
|
|
|
if (toType->IsPrimitiveType())
|
|
{
|
|
if (fromType->mTypeCode == toType->mTypeCode)
|
|
return typedVal;
|
|
|
|
if ((fromType->IsStruct()) || (fromType->IsEnum()))
|
|
{
|
|
if (fromType->mTypeParam != NULL)
|
|
{
|
|
DbgTypedValue underlyingTypedValue = typedVal;
|
|
underlyingTypedValue.mType = fromType->mTypeParam;
|
|
auto castedVal = Cast(srcNode, underlyingTypedValue, toType, explicitCast, true);
|
|
if (castedVal)
|
|
return castedVal;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fromType == toType)
|
|
return typedVal;
|
|
|
|
if ((fromType->mTypeCode == DbgType_Null) &&
|
|
(toType->mTypeCode == DbgType_Ptr))
|
|
{
|
|
DbgTypedValue val;
|
|
val.mPtr = typedVal.mPtr;
|
|
val.mType = toType;
|
|
return val;
|
|
}
|
|
|
|
auto curTypeInstance = GetCurrentType();
|
|
|
|
if ((fromType->IsCompositeType()) && ((toType->IsCompositeType() || (toType->IsInterface()))))
|
|
{
|
|
bool allowCast = false;
|
|
|
|
//TODO: Allow explicit interface casts on any object
|
|
//TODO: Insert cast checking code
|
|
|
|
int thisOffset = 0;
|
|
addr_target thisVal = typedVal.mSrcAddress;
|
|
if (TypeIsSubTypeOf(fromType, toType, &thisOffset, &thisVal))
|
|
{
|
|
allowCast = true;
|
|
}
|
|
else if ((explicitCast) && ((toType->IsInterface()) || (TypeIsSubTypeOf(toType, fromType, &thisOffset))))
|
|
{
|
|
thisOffset = -thisOffset;
|
|
allowCast = true;
|
|
}
|
|
|
|
if (fromType->IsBfPayloadEnum())
|
|
{
|
|
if (!allowCast)
|
|
{
|
|
for (auto member : fromType->mMemberList)
|
|
{
|
|
if (member->mType->Equals(toType))
|
|
{
|
|
allowCast = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allowCast)
|
|
{
|
|
DbgTypedValue val;
|
|
//val.mPtr = typedVal.mPtr + thisOffset;
|
|
val.mInt64 = typedVal.mInt64;
|
|
val.mSrcAddress = thisVal + thisOffset;
|
|
val.mType = toType;
|
|
// Doing this allows us to modify a typed value that is tied to a register through its '[base]'
|
|
if ((typedVal.mRegNum != -1) && (val.mType->GetByteCount() == typedVal.mType->GetByteCount()))
|
|
val.mRegNum = typedVal.mRegNum;
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// Payload discriminator
|
|
if ((fromType->IsBfPayloadEnum()) && (toType->IsInteger()))
|
|
{
|
|
intptr valAddr;
|
|
DbgType* valType;
|
|
DbgAddrType addrType = DbgAddrType_Value;
|
|
String findName = "$";
|
|
//BF_ASSERT(typedVal.mVariable != NULL);
|
|
if (typedVal.mVariable != NULL)
|
|
findName += typedVal.mVariable->mName;
|
|
findName += "$d";
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, GetStackFrame(), &valAddr, &valType, &addrType))
|
|
{
|
|
return ReadTypedValue(srcNode, valType, valAddr, addrType);
|
|
}
|
|
}
|
|
|
|
// Optimized composite - probably stored in register
|
|
if ((toType->IsCompositeType()) && (fromType->IsInteger()) && (explicitCast))
|
|
{
|
|
if (toType->GetByteCount() <= 8)
|
|
{
|
|
DbgTypedValue val;
|
|
val.mType = toType;
|
|
val.mInt64 = typedVal.mInt64;
|
|
val.mSrcAddress = typedVal.mSrcAddress;
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// Ptr -> const Ptr
|
|
if ((fromType->IsPointer()) && (toType->IsPointer()))
|
|
{
|
|
if (toType->mTypeParam->IsConst())
|
|
{
|
|
bool canCast = false;
|
|
auto primaryFrom = fromType->mTypeParam->GetPrimaryType();
|
|
auto primaryTo = toType->mTypeParam->mTypeParam->GetPrimaryType();
|
|
if ((primaryFrom->IsCompositeType()) || (primaryTo->IsCompositeType()))
|
|
{
|
|
if (primaryFrom == primaryTo)
|
|
canCast = true;
|
|
}
|
|
else if (primaryFrom->IsPrimitiveType())
|
|
{
|
|
canCast = primaryFrom->mTypeCode == primaryTo->mTypeCode;
|
|
}
|
|
|
|
if (canCast)
|
|
{
|
|
DbgTypedValue val = typedVal;
|
|
val.mType = toType;
|
|
return val;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((fromType->IsPointer()) && (fromType->mTypeParam->IsCompositeType()) &&
|
|
(toType->IsPointer()) && (toType->mTypeParam->IsCompositeType()))
|
|
{
|
|
auto fromInnerType = fromType->mTypeParam->RemoveModifiers();
|
|
auto toInnerType = toType->mTypeParam->RemoveModifiers();
|
|
|
|
/*DbgTypedValue fromVal;
|
|
fromVal.mType = fromInnerTytpe;
|
|
fromVal.mSrcAddress = typedVal.mPtr;
|
|
|
|
DbgTypedValue toVal = Cast(srcNode, fromVal, toInnerType, explicitCast, silentFail);
|
|
if (toVal)
|
|
{
|
|
// Back to pointer form
|
|
toVal.mType = toType;
|
|
toVal.mPtr = toVal.mSrcAddress;
|
|
toVal.mSrcAddress = 0;
|
|
}
|
|
return toVal;*/
|
|
|
|
bool allowCast = false;
|
|
|
|
//TODO: Allow explicit interface casts on any object
|
|
//TODO: Insert cast checking code
|
|
|
|
int thisOffset = 0;
|
|
addr_target thisVal = typedVal.mSrcAddress;
|
|
if (TypeIsSubTypeOf(fromInnerType, toInnerType, &thisOffset, &thisVal))
|
|
{
|
|
allowCast = true;
|
|
}
|
|
else if ((explicitCast) && ((toInnerType->IsInterface()) || (TypeIsSubTypeOf(toInnerType, fromInnerType, &thisOffset))))
|
|
{
|
|
thisOffset = -thisOffset;
|
|
allowCast = true;
|
|
}
|
|
|
|
if (allowCast)
|
|
{
|
|
DbgTypedValue toVal;
|
|
toVal.mType = toType;
|
|
toVal.mPtr = typedVal.mPtr + thisOffset;
|
|
toVal.mSrcAddress = 0;
|
|
return toVal;
|
|
}
|
|
}
|
|
|
|
|
|
// IFace -> object|IFace
|
|
if ((fromType->IsInterface()) ||
|
|
((fromType->IsPointer()) && (fromType->mTypeParam->IsInterface())))
|
|
{
|
|
if (toType->IsBfObject())
|
|
{
|
|
DbgTypedValue val;
|
|
val.mPtr = typedVal.GetPointer();
|
|
val.mType = toType;
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// Int|Ptr|Arr -> Int|Ptr|Arr
|
|
if (((fromType->IsInteger()) || (fromType->IsPointer()) || (fromType->IsSizedArray())) &&
|
|
((toType->IsInteger()) || (toType->IsPointer()) || (toType->IsSizedArray())))
|
|
{
|
|
bool allowCast = explicitCast;
|
|
if (allowCast)
|
|
{
|
|
DbgTypedValue val;
|
|
|
|
if (toType->IsSizedArray())
|
|
val.mSrcAddress = typedVal.GetPointer();
|
|
else
|
|
val.mPtr = typedVal.GetPointer();
|
|
val.mType = toType;
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// Enum -> Int
|
|
if ((((fromType->IsBfEnum()) || (fromType->IsEnum())) && (toType->IsInteger())) &&
|
|
((explicitCast) || (fromType == curTypeInstance)))
|
|
{
|
|
DbgTypedValue result = typedVal;
|
|
result.mType = toType;
|
|
return result;
|
|
}
|
|
|
|
// Int -> Enum
|
|
if (((fromType->IsInteger()) && ((toType->IsBfEnum()) || (toType->IsEnum()))) &&
|
|
((explicitCast) || (toType == curTypeInstance)))
|
|
{
|
|
DbgTypedValue result = typedVal;
|
|
result.mType = toType;
|
|
return result;
|
|
}
|
|
|
|
// TypedPrimitive -> Primitive
|
|
if ((fromType->IsTypedPrimitive()) && (toType->IsPrimitiveType()))
|
|
{
|
|
DbgTypedValue fromValue = typedVal;
|
|
fromValue.mType = fromType->GetBaseType();
|
|
return Cast(srcNode, fromValue, toType, explicitCast);
|
|
}
|
|
|
|
// TypedPrimitive -> TypedPrimitive
|
|
if ((fromType->IsTypedPrimitive()) && (toType->IsTypedPrimitive()))
|
|
{
|
|
DbgTypedValue fromValue = typedVal;
|
|
fromValue.mType = fromType->GetBaseType();
|
|
DbgTypedValue primTypedVal = Cast(srcNode, fromValue, toType->GetBaseType(), explicitCast, silentFail);
|
|
if (primTypedVal)
|
|
{
|
|
primTypedVal.mType = toType;
|
|
return primTypedVal;
|
|
}
|
|
}
|
|
|
|
// Primitive -> TypedPrimitive
|
|
if ((fromType->IsPrimitiveType()) && (toType->IsTypedPrimitive()))
|
|
{
|
|
DbgTypedValue primTypedVal = Cast(srcNode, typedVal, toType->GetBaseType(), true);
|
|
if (primTypedVal)
|
|
{
|
|
primTypedVal.mType = toType;
|
|
return primTypedVal;
|
|
}
|
|
}
|
|
|
|
|
|
if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType()))
|
|
{
|
|
DbgTypeCode fromTypeCode = fromType->mTypeCode;
|
|
DbgTypeCode toTypeCode = toType->mTypeCode;
|
|
|
|
if ((toType->IsInteger()) && (fromType->IsIntegral()))
|
|
{
|
|
// Allow constant ints to be implicitly shrunk if they fit
|
|
if (typedVal.mIsLiteral)
|
|
{
|
|
int64 srcVal = typedVal.GetInt64();
|
|
/*if (toType->IsSigned())
|
|
{
|
|
int64 minVal = -(1LL << (8 * toType->mSize - 1));
|
|
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}
|
|
else if (toType->mSize == 8) // ulong
|
|
{
|
|
if (srcVal > 0)
|
|
explicitCast = true;
|
|
}
|
|
else
|
|
{
|
|
int64 minVal = 0;
|
|
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}*/
|
|
|
|
if (toType->IsChar())
|
|
{
|
|
if (srcVal == 0)
|
|
explicitCast = true;
|
|
}
|
|
else if ((fromType->IsChar()) && (!toType->IsChar()))
|
|
{
|
|
// Never allow this
|
|
}
|
|
else if ((fromTypeCode == BfTypeCode_UInt64) && (srcVal < 0))
|
|
{
|
|
// There's nothing that this could fit into
|
|
}
|
|
else if (toType->IsSigned())
|
|
{
|
|
int64 minVal = -(1LL << (8 * toType->mSize - 1));
|
|
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}
|
|
else if (toType->mSize == 8) // ulong
|
|
{
|
|
if (srcVal >= 0)
|
|
explicitCast = true;
|
|
}
|
|
else
|
|
{
|
|
int64 minVal = 0;
|
|
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
|
|
if ((srcVal >= minVal) && (srcVal <= maxVal))
|
|
explicitCast = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
DbgTypedValue result;
|
|
result.mType = toType;
|
|
if (((fromType->IsInteger()) || (fromType->IsChar())) &&
|
|
(toType->IsInteger()) || (toType->IsChar()))
|
|
{
|
|
result.mInt64 = typedVal.GetInt64();
|
|
if (explicitCast)
|
|
return result;
|
|
}
|
|
|
|
switch (toTypeCode)
|
|
{
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
return result;
|
|
}
|
|
break;
|
|
|
|
case DbgType_i16:
|
|
case DbgType_SChar16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_u16:
|
|
case DbgType_UChar16:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_SChar:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_UChar:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_i32:
|
|
case DbgType_SChar32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_UChar32:
|
|
case DbgType_u32:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_i64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_u64:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar32:
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_Single:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_i64:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
result.mSingle = typedVal.GetInt64();
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_u64:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
result.mSingle = typedVal.GetInt64();
|
|
return result;
|
|
}
|
|
break;
|
|
case DbgType_Double:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_i64:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
result.mDouble = typedVal.GetInt64();
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_u64:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
result.mDouble = typedVal.GetInt64();
|
|
return result;
|
|
case DbgType_Single:
|
|
result.mDouble = typedVal.mSingle;
|
|
return result;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (explicitCast)
|
|
{
|
|
if (toType->IsInteger())
|
|
{
|
|
if (typedVal.mType->IsInteger())
|
|
{
|
|
return result;
|
|
}
|
|
else if (typedVal.mType->IsFloat())
|
|
{
|
|
if (typedVal.mType->mTypeCode == DbgType_Single)
|
|
result.mInt64 = typedVal.mSingle;
|
|
else
|
|
result.mInt64 = (int64)typedVal.mDouble;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
switch (toTypeCode)
|
|
{
|
|
case DbgType_Single:
|
|
switch (fromTypeCode)
|
|
{
|
|
case DbgType_i8:
|
|
case DbgType_i16:
|
|
case DbgType_i32:
|
|
case DbgType_SChar:
|
|
case DbgType_SChar16:
|
|
case DbgType_SChar32:
|
|
result.mSingle = (float)typedVal.GetInt64();
|
|
return result;
|
|
case DbgType_u8:
|
|
case DbgType_u16:
|
|
case DbgType_u32:
|
|
case DbgType_UChar:
|
|
case DbgType_UChar16:
|
|
case DbgType_UChar32:
|
|
result.mSingle = (float)typedVal.GetInt64();
|
|
return result;
|
|
case DbgType_Double:
|
|
result.mSingle = (float)typedVal.mDouble;
|
|
return result;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fromType->mTypeCode == DbgType_TypeDef)
|
|
{
|
|
DbgTypedValue realTypedVal = typedVal;
|
|
realTypedVal.mType = fromType->mTypeParam;
|
|
return Cast(srcNode, realTypedVal, toType, explicitCast, silentFail);
|
|
}
|
|
|
|
if (toType->mTypeCode == DbgType_TypeDef)
|
|
return Cast(srcNode, typedVal, toType->mTypeParam, explicitCast, silentFail);
|
|
|
|
if (!silentFail)
|
|
{
|
|
auto language = GetLanguage();
|
|
|
|
const char* errStr = explicitCast ?
|
|
"Unable to cast '%s' to '%s'" :
|
|
"Unable to implicitly cast '%s' to '%s'";
|
|
Fail(StrFormat(errStr, fromType->ToString(language).c_str(), toType->ToString(language).c_str()), srcNode);
|
|
}
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
bool DbgExprEvaluator::HasField(DbgType* curCheckType, const StringImpl& fieldName)
|
|
{
|
|
curCheckType = curCheckType->RemoveModifiers();
|
|
curCheckType = curCheckType->GetPrimaryType();
|
|
curCheckType->PopulateType();
|
|
|
|
for (auto checkMember : curCheckType->mMemberList)
|
|
{
|
|
if (checkMember->mName == NULL)
|
|
{
|
|
auto checkResult = HasField(checkMember->mType, fieldName);
|
|
if (checkResult)
|
|
return checkResult;
|
|
}
|
|
else if (checkMember->mName == fieldName)
|
|
return true;
|
|
}
|
|
|
|
for (auto baseTypeEntry : curCheckType->mBaseTypes)
|
|
{
|
|
auto baseType = baseTypeEntry->mBaseType;
|
|
auto result = HasField(baseType, fieldName);
|
|
if (result)
|
|
return result;
|
|
}
|
|
|
|
//if (wantsStatic)
|
|
{
|
|
// Look for statics in anonymous inner classes
|
|
for (auto checkSubType : curCheckType->mSubTypeList)
|
|
{
|
|
if (checkSubType->mTypeName == NULL)
|
|
{
|
|
auto result = HasField(checkSubType, fieldName);
|
|
if (result)
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (auto altType : curCheckType->mAlternates)
|
|
{
|
|
auto result = HasField(altType, fieldName);
|
|
if (result)
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::DoLookupField(BfAstNode* targetSrc, DbgTypedValue target, DbgType* curCheckType, const StringImpl& fieldName, CPUStackFrame* stackFrame, bool allowImplicitThis)
|
|
{
|
|
bool wantsStatic = (!target) || (target.mHasNoValue);
|
|
auto flavor = GetFlavor();
|
|
auto language = GetLanguage();
|
|
if (language != DbgLanguage_Beef)
|
|
{
|
|
// C allows static lookups by non-static member reference
|
|
wantsStatic = true;
|
|
}
|
|
|
|
curCheckType = curCheckType->RemoveModifiers();
|
|
curCheckType = curCheckType->GetPrimaryType();
|
|
curCheckType->PopulateType();
|
|
|
|
//BfLogDbgExpr("DoLookupField %s %s Priority=%d\n", fieldName.c_str(), curCheckType->mName, curCheckType->mPriority);
|
|
|
|
if ((curCheckType->mNeedsGlobalsPopulated) && ((curCheckType->IsNamespace()) || (curCheckType->IsRoot())))
|
|
{
|
|
// Global variables don't show up in any type declaration like static fields do, so we need to populate here
|
|
mDbgModule->PopulateTypeGlobals(curCheckType);
|
|
}
|
|
|
|
/*auto checkMember = curCheckType->mMemberList.mHead;
|
|
while (checkMember != NULL)*/
|
|
|
|
String findFieldName = fieldName;
|
|
if ((language == DbgLanguage_Beef) && (flavor == DbgFlavor_MS) && (target.mHasNoValue))
|
|
{
|
|
//if ((curCheckType->IsRoot()) || (curCheckType->IsNamespace()))
|
|
//findFieldName.insert(0, "bf__");
|
|
}
|
|
|
|
//for (auto checkMember : curCheckType->mMemberList)
|
|
auto nextMember = curCheckType->mMemberList.mHead;
|
|
while (nextMember != NULL)
|
|
{
|
|
auto checkMember = nextMember;
|
|
nextMember = checkMember->mNext;
|
|
|
|
if (checkMember->mName == NULL)
|
|
{
|
|
//TODO: Check inside anonymous type
|
|
DbgTypedValue innerTarget;
|
|
|
|
addr_target targetPtr = target.mSrcAddress;
|
|
if ((target.mType != NULL) && (target.mType->HasPointer()))
|
|
targetPtr = target.mPtr;
|
|
innerTarget.mSrcAddress = targetPtr + checkMember->mMemberOffset;
|
|
/*if (target.mPtr != 0)
|
|
innerTarget.mPtr = target.mPtr + checkMember->mMemberOffset;*/
|
|
innerTarget.mType = checkMember->mType;
|
|
|
|
auto checkResult = LookupField(targetSrc, innerTarget, fieldName);
|
|
if (checkResult)
|
|
return checkResult;
|
|
}
|
|
else if (checkMember->mName == findFieldName)
|
|
{
|
|
//BfLogDbgExpr(" Got Match\n");
|
|
|
|
//TODO:
|
|
/*if (field->mIsConst)
|
|
{
|
|
|
|
if (fieldInstance->mStaticValue == NULL)
|
|
mModule->ResolveConstField(curCheckType, field);
|
|
return DbgTypedValue(fieldInstance->mStaticValue, fieldInstance->mType);
|
|
}*/
|
|
|
|
//checkMember->mMemberOffset
|
|
|
|
if (mReferenceId != NULL)
|
|
{
|
|
if (curCheckType->IsRoot())
|
|
*mReferenceId = fieldName;
|
|
else if (curCheckType->IsEnum())
|
|
*mReferenceId = curCheckType->ToString();
|
|
else
|
|
*mReferenceId = curCheckType->ToString() + "." + fieldName;
|
|
}
|
|
|
|
if ((checkMember->mLocationLen == 0) && (checkMember->mIsStatic) && (!checkMember->mIsConst))
|
|
{
|
|
if (checkMember->mCompileUnit->mDbgModule->mDbgFlavor == DbgFlavor_MS)
|
|
{
|
|
if (curCheckType->mNeedsGlobalsPopulated)
|
|
{
|
|
mDbgModule->PopulateTypeGlobals(curCheckType);
|
|
return DoLookupField(targetSrc, target, curCheckType, fieldName, stackFrame, allowImplicitThis);
|
|
}
|
|
|
|
/*String memberName = curCheckType->ToString() + "::" + checkMember->mName;
|
|
mDbgModule->ParseSymbolData();
|
|
auto entry = mDbgModule->mSymbolNameMap.Find(memberName.c_str());
|
|
if (entry != NULL)
|
|
{
|
|
auto sym = entry->mValue;
|
|
int locLen = 1 + sizeof(addr_target);
|
|
uint8* locData = checkMember->mCompileUnit->mDbgModule->mAlloc.AllocBytes(locLen);
|
|
locData[0] = DW_OP_addr_noRemap;
|
|
*((addr_target*)(locData + 1)) = sym->mAddress;
|
|
checkMember->mLocationData = locData;
|
|
checkMember->mLocationLen = locLen;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT("Static field not found" == 0);
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
|
|
//BF_FATAL("Unhandled");
|
|
|
|
//TODO: Is this needed anymore since we have 'primary types'?
|
|
/*auto altType = mDebugTarget->FindType(target.mType->ToString(true));
|
|
if (altType != NULL)
|
|
{
|
|
auto altChildNode = altType->mAlternates.mHead;
|
|
while ((altType != NULL) && (checkMember->mLocationLen == 0))
|
|
{
|
|
for (auto altMember : altType->mMemberList)
|
|
{
|
|
if (strcmp(altMember->mName, checkMember->mName))
|
|
{
|
|
if (altMember->mLocationData != NULL)
|
|
{
|
|
checkMember->mLinkName = altMember->mLinkName;
|
|
checkMember->mLocationData = altMember->mLocationData;
|
|
checkMember->mLocationLen = altMember->mLocationLen;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (altChildNode != NULL)
|
|
{
|
|
altType = altChildNode->mValue;
|
|
altChildNode = altChildNode->mNext;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}*/
|
|
}
|
|
}
|
|
|
|
if (checkMember->mLocationLen != 0)
|
|
{
|
|
DbgAddrType addrType;
|
|
intptr valAddr = checkMember->mCompileUnit->mDbgModule->EvaluateLocation(NULL, checkMember->mLocationData, checkMember->mLocationLen, stackFrame, &addrType);
|
|
if ((language == DbgLanguage_Beef) && (checkMember->mType->IsConst()) && (checkMember->mType->mTypeParam->IsSizedArray()))
|
|
{
|
|
// We need an extra deref
|
|
valAddr = mDebugger->ReadMemory<addr_target>(valAddr);
|
|
}
|
|
|
|
if (checkMember->mIsConst)
|
|
addrType = DbgAddrType_Value;
|
|
return ReadTypedValue(targetSrc, checkMember->mType, valAddr, addrType);
|
|
}
|
|
else if (checkMember->mIsConst)
|
|
{
|
|
return ReadTypedValue(targetSrc, checkMember->mType, (uint64)&checkMember->mConstValue, DbgAddrType_Local);
|
|
}
|
|
else if (checkMember->mIsStatic)
|
|
{
|
|
if (curCheckType->mNeedsGlobalsPopulated)
|
|
{
|
|
mDbgModule->PopulateTypeGlobals(curCheckType);
|
|
nextMember = curCheckType->mMemberList.mHead;
|
|
continue;
|
|
}
|
|
|
|
Fail("Static variable not found, may have be optimized out", targetSrc);
|
|
//BfLogDbgExpr(" Static variable optimized out\n");
|
|
}
|
|
else
|
|
{
|
|
if ((allowImplicitThis) && (target.mPtr == 0))
|
|
target = GetThis();
|
|
|
|
if (!target)
|
|
{
|
|
Fail("Cannot reference a non-static member from a static method", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
addr_target targetPtr = target.mSrcAddress;
|
|
|
|
DbgTypedValue typedValue;
|
|
if (targetPtr == -1)
|
|
{
|
|
LookupSplatMember(target, fieldName);
|
|
typedValue = mResult;
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else if ((targetPtr == 0) && (target.mPtr != 0) && (!target.mType->HasPointer()))
|
|
{
|
|
typedValue = ReadTypedValue(targetSrc, checkMember->mType, (uint64)&target.mPtr + checkMember->mMemberOffset, DbgAddrType_Local);
|
|
}
|
|
else
|
|
{
|
|
if (target.mType->HasPointer())
|
|
targetPtr = target.mPtr;
|
|
|
|
typedValue = ReadTypedValue(targetSrc, checkMember->mType, targetPtr + checkMember->mMemberOffset, DbgAddrType_Target);
|
|
}
|
|
|
|
if (checkMember->mBitSize != 0)
|
|
{
|
|
int expectedShift = (checkMember->mType->mSize * 8) - checkMember->mBitSize;
|
|
int shift = expectedShift - checkMember->mBitOffset;
|
|
|
|
typedValue.mUInt64 = typedValue.mUInt64 >> shift;
|
|
uint64 mask = 0;
|
|
for (int i = 0; i < checkMember->mBitSize; i++)
|
|
mask |= (1ULL << i);
|
|
typedValue.mUInt64 &= mask;
|
|
|
|
if ((checkMember->mType->IsSigned()) && ((typedValue.mUInt64 & (1LL << (checkMember->mBitSize - 1))) != 0))
|
|
{
|
|
// Sign extend
|
|
typedValue.mUInt64 |= ~mask;
|
|
}
|
|
}
|
|
|
|
return typedValue;
|
|
}
|
|
}
|
|
|
|
//checkMember = checkMember->mNext;
|
|
}
|
|
|
|
BF_ASSERT((mPropGet == NULL) && (mPropSet == NULL));
|
|
|
|
if (curCheckType->mMethodList.IsEmpty())
|
|
{
|
|
bool hasProps = false;
|
|
for (auto methodNameEntry : curCheckType->mMethodNameList)
|
|
{
|
|
const char* methodName = methodNameEntry->mName;
|
|
if (((methodName[0] == 'g') || (methodName[0] == 's')) &&
|
|
((strncmp(methodName, "get__", 5) == 0) ||
|
|
(strncmp(methodName, "set__", 5) == 0)))
|
|
{
|
|
if (strcmp(methodName + 5, fieldName.c_str()) == 0)
|
|
{
|
|
curCheckType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId);
|
|
methodNameEntry->mCompileUnitId = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int pass = 0; pass < 2; pass++)
|
|
{
|
|
for (auto method : curCheckType->mMethodList)
|
|
{
|
|
if (method->mName != NULL)
|
|
{
|
|
if (((method->mName[0] == 'g') || (method->mName[0] == 's')) &&
|
|
((strncmp(method->mName, "get__", 5) == 0) ||
|
|
(strncmp(method->mName, "set__", 5) == 0)))
|
|
{
|
|
if (strcmp(method->mName + 5, fieldName.c_str()) == 0)
|
|
{
|
|
bool isGetter = method->mName[0] == 'g';
|
|
|
|
method->PopulateSubprogram();
|
|
|
|
if (fieldName.IsEmpty())
|
|
{
|
|
int expectedParams = (int)mIndexerValues.size();
|
|
if (!isGetter)
|
|
expectedParams++;
|
|
if (method->GetParamCount() != expectedParams)
|
|
continue;
|
|
}
|
|
|
|
DbgSubprogram*& subprogramRef = isGetter ? mPropGet : mPropSet;
|
|
|
|
if ((subprogramRef == NULL) ||
|
|
((!method->mHasThis) && (wantsStatic)) ||
|
|
((method->mHasThis) && (!wantsStatic || allowImplicitThis)))
|
|
{
|
|
if ((method->mHasThis) && (allowImplicitThis) && (!target))
|
|
{
|
|
target = GetThis();
|
|
}
|
|
|
|
if (subprogramRef == NULL)
|
|
subprogramRef = method;
|
|
else if ((method->mVTableLoc != -1) || (subprogramRef->mVTableLoc == -1))
|
|
subprogramRef = method;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool loadedCompileUnit = false;
|
|
for (int methodIdx = 0; methodIdx < 2; methodIdx++)
|
|
{
|
|
auto method = (methodIdx == 0) ? mPropGet : mPropSet;
|
|
if (method == NULL)
|
|
continue;
|
|
|
|
if (method->mBlock.mLowPC == 0)
|
|
{
|
|
if (!curCheckType->mHasGlobalsPopulated)
|
|
mDbgModule->PopulateTypeGlobals(curCheckType);
|
|
for (auto methodNameEntry : curCheckType->mMethodNameList)
|
|
{
|
|
const char* methodName = methodNameEntry->mName;
|
|
if ((methodNameEntry->mCompileUnitId >= 0) && (strcmp(methodName, method->mName) == 0))
|
|
{
|
|
curCheckType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId);
|
|
loadedCompileUnit = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!loadedCompileUnit)
|
|
break;
|
|
}
|
|
|
|
// Found a result
|
|
if ((mPropGet != NULL) || (mPropSet != NULL))
|
|
{
|
|
mPropSrc = targetSrc;
|
|
mPropTarget = target;
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
//TODO:
|
|
/*for (auto prop : curCheckType->mTypeDef->mProperties)
|
|
{
|
|
if (prop->mName == fieldName)
|
|
{
|
|
mModule->Fail("Cannot reference property");
|
|
return DbgTypedValue();
|
|
}
|
|
}*/
|
|
|
|
|
|
//curCheckType = curCheckType->GetBaseType();
|
|
|
|
for (auto baseTypeEntry : curCheckType->mBaseTypes)
|
|
{
|
|
auto baseType = baseTypeEntry->mBaseType;
|
|
|
|
DbgTypedValue baseTarget = target;
|
|
addr_target* ptrRef = NULL;
|
|
if ((baseTarget.mPtr != 0) && (target.mType->HasPointer()))
|
|
ptrRef = &baseTarget.mPtr;
|
|
else if (baseTarget.mSrcAddress != 0)
|
|
ptrRef = &baseTarget.mSrcAddress;
|
|
|
|
if (ptrRef != NULL)
|
|
{
|
|
if (baseTypeEntry->mVTableOffset != -1)
|
|
{
|
|
addr_target vtableAddr = mDebugger->ReadMemory<addr_target>(target.mPtr);
|
|
int32 virtThisOffset = mDebugger->ReadMemory<int32>(vtableAddr + baseTypeEntry->mVTableOffset * sizeof(int32));
|
|
*ptrRef += virtThisOffset;
|
|
}
|
|
else
|
|
*ptrRef += baseTypeEntry->mThisOffset;
|
|
}
|
|
|
|
//BF_ASSERT(baseTypeEntry->mVTableOffset == -1);
|
|
auto result = DoLookupField(targetSrc, baseTarget, baseType, fieldName, stackFrame, allowImplicitThis);
|
|
if (result)
|
|
return result;
|
|
}
|
|
|
|
if (wantsStatic)
|
|
{
|
|
// Look for statics in anonymous inner classes
|
|
for (auto checkSubType : curCheckType->mSubTypeList)
|
|
{
|
|
bool includeSubType = ((checkSubType->mTypeName == NULL) || (checkSubType->IsGlobalsContainer()));
|
|
if ((language == DbgLanguage_C) && (checkSubType->IsEnum()))
|
|
includeSubType = true;
|
|
if (includeSubType)
|
|
{
|
|
if ((checkSubType->IsGlobalsContainer()) && (checkSubType->mPriority <= DbgTypePriority_Normal))
|
|
continue;
|
|
auto rawCheckSubType = checkSubType->RemoveModifiers();
|
|
auto result = DoLookupField(targetSrc, DbgTypedValue(), rawCheckSubType, fieldName, stackFrame, false);
|
|
if (result)
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//Turn alternates back on!
|
|
/*for (auto altType : curCheckType->mAlternates)
|
|
{
|
|
auto result = DoLookupField(targetSrc, DbgTypedValue(), altType, fieldName, stackFrame, false);
|
|
if (result)
|
|
return result;
|
|
}*/
|
|
}
|
|
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::LookupField(BfAstNode* targetSrc, DbgTypedValue target, const StringImpl& fieldName)
|
|
{
|
|
CPUStackFrame* stackFrame = GetStackFrame();
|
|
|
|
DbgType* curCheckType = NULL;
|
|
/*if (currentMethod != NULL)
|
|
curCheckType = currentMethod->mParentType;*/
|
|
DbgType* curMethodType = curCheckType;
|
|
|
|
if (target)
|
|
{
|
|
if (target.mType->IsPrimitiveType())
|
|
{
|
|
curCheckType = mDbgModule->GetPrimitiveStructType(target.mType->mTypeCode);
|
|
if (curCheckType == NULL)
|
|
return DbgTypedValue();
|
|
}
|
|
else
|
|
{
|
|
curCheckType = target.mType;
|
|
}
|
|
BF_ASSERT(curCheckType != NULL);
|
|
}
|
|
else if (target.mType != NULL)
|
|
curCheckType = target.mType;
|
|
|
|
if (curCheckType != NULL)
|
|
{
|
|
curCheckType = curCheckType->RemoveModifiers();
|
|
if (curCheckType->mTypeCode == DbgType_Ptr)
|
|
curCheckType = curCheckType->mTypeParam;
|
|
}
|
|
|
|
bool allowImplicitThis = false;
|
|
if (curCheckType == NULL)
|
|
{
|
|
auto currentMethod = GetCurrentMethod();
|
|
if (currentMethod != NULL)
|
|
{
|
|
curCheckType = currentMethod->GetTargetType();
|
|
allowImplicitThis = currentMethod->mHasThis;
|
|
}
|
|
}
|
|
|
|
//SetAndRestoreValue<DbgType*> prevTypeInstance(curMethodType, curCheckType);
|
|
|
|
if (curCheckType == NULL)
|
|
return DbgTypedValue();
|
|
|
|
return DoLookupField(targetSrc, target, curCheckType, fieldName, stackFrame, allowImplicitThis);
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfAstNode* node)
|
|
{
|
|
Fail("Invalid debug expression", node);
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::ReadTypedValue(BfAstNode* targetSrc, DbgType* dbgType, uint64 valAddr, DbgAddrType addrType)
|
|
{
|
|
bool failedReadMemory = false;
|
|
|
|
if (addrType == DbgAddrType_Alias)
|
|
{
|
|
String findName = (const char*)valAddr;
|
|
|
|
if (targetSrc == NULL)
|
|
return DbgTypedValue();
|
|
|
|
auto source = targetSrc->GetSourceData();
|
|
auto bfParser = source->ToParser();
|
|
if (bfParser == NULL)
|
|
return DbgTypedValue();
|
|
|
|
auto identifierNode = source->mAlloc.Alloc<BfIdentifierNode>();
|
|
int srcStart = bfParser->AllocChars(findName.length());
|
|
memcpy((char*)source->mSrc + srcStart, findName.c_str(), findName.length());
|
|
identifierNode->Init(srcStart, srcStart, srcStart + findName.length());
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
auto result = LookupIdentifier(identifierNode);
|
|
if (result)
|
|
return result;
|
|
|
|
// Allow lookup in calling method if we are inlined (for mixin references)
|
|
auto currentMethod = GetCurrentMethod();
|
|
if ((currentMethod != NULL) && (currentMethod->mInlineeInfo != NULL))
|
|
{
|
|
SetAndRestoreValue<int> preCallstackIdx(mCallStackIdx);
|
|
mCallStackIdx++;
|
|
result = LookupIdentifier(identifierNode);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if (addrType == DbgAddrType_LocalSplat)
|
|
{
|
|
DbgTypedValue val;
|
|
val.mVariable = (DbgVariable*)valAddr;
|
|
val.mType = dbgType;
|
|
val.mSrcAddress = -1;
|
|
val.mIsReadOnly = true;
|
|
return val;
|
|
}
|
|
|
|
if (addrType == DbgAddrType_TargetDeref)
|
|
{
|
|
DbgTypedValue result = ReadTypedValue(targetSrc, dbgType, valAddr, DbgAddrType_Target);
|
|
if ((result.mType != NULL) && (result.mType->IsPointer()))
|
|
{
|
|
result.mType = result.mType->mTypeParam;
|
|
result.mSrcAddress = result.mPtr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (addrType == DbgAddrType_NoValue)
|
|
{
|
|
mPassInstance->Fail(StrFormat("No value", valAddr));
|
|
return DbgTypedValue();
|
|
}
|
|
else if (addrType == DbgAddrType_OptimizedOut)
|
|
{
|
|
mPassInstance->Fail(StrFormat("Optimized out", valAddr));
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
bool hadRef = false;
|
|
DbgType* origDwType = dbgType;
|
|
while (true)
|
|
{
|
|
if (dbgType->mTypeCode == DbgType_Const)
|
|
{
|
|
dbgType = mDbgModule->GetInnerTypeOrVoid(dbgType);
|
|
}
|
|
else if ((dbgType->mTypeCode == DbgType_TypeDef) || (dbgType->mTypeCode == DbgType_Volatile))
|
|
{
|
|
dbgType = mDbgModule->GetInnerTypeOrVoid(dbgType);
|
|
}
|
|
else if ((dbgType->mTypeCode == DbgType_Ref) || (dbgType->mTypeCode == DbgType_RValueReference))
|
|
{
|
|
hadRef = true;
|
|
auto innerType = dbgType->mTypeParam;
|
|
if ((!innerType->IsCompositeType()) || (addrType == DbgAddrType_Target))
|
|
{
|
|
if (addrType == DbgAddrType_Target)
|
|
valAddr = mDebugger->ReadMemory<addr_target>(valAddr);
|
|
if (addrType == DbgAddrType_Register)
|
|
{
|
|
auto registers = GetRegisters();
|
|
valAddr = registers->mIntRegsArray[valAddr];
|
|
}
|
|
|
|
//valIsAddr = true;
|
|
addrType = DbgAddrType_Target;
|
|
//local = false;
|
|
}
|
|
else
|
|
{
|
|
//local = false;
|
|
//valIsAddr = true;
|
|
if (addrType == DbgAddrType_Register)
|
|
{
|
|
auto registers = GetRegisters();
|
|
valAddr = registers->mIntRegsArray[valAddr];
|
|
}
|
|
addrType = DbgAddrType_Target;
|
|
}
|
|
dbgType = dbgType->mTypeParam;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
dbgType = dbgType->GetPrimaryType();
|
|
|
|
if (dbgType->GetByteCount() == 0)
|
|
{
|
|
DbgTypedValue result;
|
|
result.mType = dbgType;
|
|
return result;
|
|
}
|
|
|
|
if (dbgType->mTypeCode == DbgType_Bitfield)
|
|
{
|
|
DbgType* underlyingType = dbgType->mTypeParam;
|
|
DbgTypedValue result = ReadTypedValue(targetSrc, dbgType->mTypeParam, valAddr, addrType);
|
|
result.mType = dbgType;
|
|
|
|
auto dbgBitfieldType = (DbgBitfieldType*)dbgType;
|
|
|
|
result.mUInt64 = result.mUInt64 >> dbgBitfieldType->mPosition;
|
|
|
|
uint64 mask = ((uint64)1 << dbgBitfieldType->mLength) - 1;
|
|
result.mUInt64 &= mask;
|
|
|
|
if ((underlyingType->IsSigned()) && ((result.mUInt64 & (1LL << (dbgBitfieldType->mLength - 1))) != 0))
|
|
{
|
|
// Sign extend
|
|
result.mUInt64 |= ~mask;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool local = addrType == DbgAddrType_Local;
|
|
bool valIsAddr = (addrType == DbgAddrType_Local) || (addrType == DbgAddrType_Target);
|
|
|
|
DbgTypedValue result;
|
|
result.mType = origDwType;
|
|
|
|
if (addrType == DbgAddrType_Register)
|
|
{
|
|
result.mRegNum = (int)valAddr;
|
|
|
|
auto registers = GetRegisters();
|
|
if (result.mRegNum < CPURegisters::kNumIntRegs)
|
|
result.mUInt64 = registers->mIntRegsArray[result.mRegNum];
|
|
else if ((result.mRegNum >= CPUReg_XMMREG_FIRST) && (result.mRegNum <= CPUReg_XMMREG_LAST))
|
|
{
|
|
auto dwType = origDwType->RemoveModifiers();
|
|
if (dwType->IsTypedPrimitive())
|
|
dwType = dwType->GetUnderlyingType();
|
|
if (dwType->mTypeCode == DbgType_Single)
|
|
result.mSingle = *(float*)((float*)registers->mXmmRegsArray + (result.mRegNum - CPUReg_XMMREG_FIRST));
|
|
else if (dwType->mTypeCode == DbgType_Double)
|
|
result.mDouble = *(double*)((float*)registers->mXmmRegsArray + (result.mRegNum - CPUReg_XMMREG_FIRST));
|
|
else
|
|
BF_ASSERT("Not implemented" == 0);
|
|
}
|
|
|
|
auto primaryType = result.mType->GetPrimaryType();
|
|
result.mType = primaryType;
|
|
|
|
// In release builds, VC will place int-sized structs into a single register. This code was breaking that.
|
|
/*if ((primaryType->IsCompositeType()) && (!primaryType->IsTypedPrimitive()))
|
|
{
|
|
result.mSrcAddress = result.mPtr;
|
|
result.mPtr = 0;
|
|
}*/
|
|
|
|
return result;
|
|
}
|
|
|
|
if (dbgType->mTypeCode == DbgType_Bitfield)
|
|
dbgType = dbgType->mTypeParam;
|
|
|
|
switch (dbgType->mTypeCode)
|
|
{
|
|
case DbgType_Void:
|
|
break;
|
|
|
|
case DbgType_Bool:
|
|
case DbgType_i8:
|
|
case DbgType_u8:
|
|
case DbgType_SChar:
|
|
case DbgType_UChar:
|
|
if (valIsAddr)
|
|
result.mInt8 = mDebugger->ReadMemory<int8>(valAddr, local, &failedReadMemory);
|
|
else
|
|
result.mUInt8 = (uint8)valAddr;
|
|
break;
|
|
case DbgType_i16:
|
|
case DbgType_u16:
|
|
case DbgType_SChar16:
|
|
case DbgType_UChar16:
|
|
if (valIsAddr)
|
|
result.mInt16 = mDebugger->ReadMemory<int16>(valAddr, local, &failedReadMemory);
|
|
else
|
|
result.mUInt16 = (uint16)valAddr;
|
|
break;
|
|
case DbgType_i32:
|
|
case DbgType_u32:
|
|
case DbgType_SChar32:
|
|
case DbgType_UChar32:
|
|
if (valIsAddr)
|
|
result.mInt32 = mDebugger->ReadMemory<int32>(valAddr, local, &failedReadMemory);
|
|
else
|
|
result.mUInt32 = (uint32)valAddr;
|
|
break;
|
|
case DbgType_i64:
|
|
case DbgType_u64:
|
|
if (valIsAddr)
|
|
result.mInt64 = mDebugger->ReadMemory<int64>(valAddr, local, &failedReadMemory);
|
|
else
|
|
result.mUInt64 = (uint64)valAddr;
|
|
break;
|
|
|
|
case DbgType_Single:
|
|
result.mSingle = mDebugger->ReadMemory<float>(valAddr, local, &failedReadMemory);
|
|
break;
|
|
case DbgType_Double:
|
|
result.mDouble = mDebugger->ReadMemory<double>(valAddr, local, &failedReadMemory);
|
|
break;
|
|
|
|
case DbgType_Struct:
|
|
case DbgType_Class:
|
|
case DbgType_Union:
|
|
case DbgType_SizedArray:
|
|
if (((dbgType->IsTypedPrimitive()) || (local)) && (dbgType->GetByteCount() <= 8))
|
|
{
|
|
mDebugger->ReadMemory(valAddr, dbgType->GetByteCount(), &result.mInt64, local);
|
|
result.mSrcAddress = (addr_target)valAddr;
|
|
}
|
|
else
|
|
{
|
|
BF_ASSERT(!local);
|
|
if (hadRef)
|
|
result.mPtr = (addr_target)valAddr;
|
|
else
|
|
result.mSrcAddress = (addr_target)valAddr;
|
|
}
|
|
//result.mPtr = (addr_target)valAddr;
|
|
break;
|
|
|
|
//TODO: Why was it treated as a pointer
|
|
//case DbgType_SizedArray:
|
|
case DbgType_Ptr:
|
|
if (valIsAddr)
|
|
result.mPtr = mDebugger->ReadMemory<addr_target>(valAddr, local, &failedReadMemory);
|
|
else
|
|
result.mPtr = valAddr;
|
|
break;
|
|
|
|
case DbgType_Enum:
|
|
result = ReadTypedValue(targetSrc, dbgType->mTypeParam, valAddr, addrType);
|
|
if (result)
|
|
result.mType = dbgType;
|
|
break;
|
|
|
|
case DbgType_Subroutine:
|
|
{
|
|
auto funcPtr = result.mPtr;
|
|
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf))
|
|
{
|
|
#ifdef BF_DBG_32
|
|
if ((symbolName.length() > 0) && (symbolName[0] == '_'))
|
|
symbolName = symbolName.Substring(1);
|
|
#endif
|
|
static String demangledName;
|
|
demangledName = BfDemangler::Demangle(symbolName, dbgType->GetLanguage());
|
|
|
|
DbgTypedValue result;
|
|
result.mCharPtr = demangledName.c_str();
|
|
}
|
|
else
|
|
{
|
|
result.mCharPtr = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (mPassInstance != NULL)
|
|
mPassInstance->Fail("Invalid data type");
|
|
}
|
|
|
|
if ((!local) && (valIsAddr))
|
|
{
|
|
result.mSrcAddress = valAddr;
|
|
}
|
|
|
|
if ((failedReadMemory) && (!mValidateOnly))
|
|
{
|
|
if (mPassInstance == NULL)
|
|
return DbgTypedValue();
|
|
|
|
if (addrType == DbgAddrType_NoValue)
|
|
mPassInstance->Fail("No value");
|
|
else if ((valAddr == 0) && (local))
|
|
mPassInstance->Fail("Optimized out");
|
|
else if (!mBlockedSideEffects)
|
|
mPassInstance->Fail(StrFormat("Failed to read from address %s", EncodeDataPtr(valAddr, true).c_str()));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DbgExprEvaluator::CheckTupleCreation(addr_target receiveAddr, BfAstNode* targetSrc, DbgType* tupleType, const BfSizedArray<BfExpression*>& argValues, BfSizedArray<BfTupleNameNode*>* names)
|
|
{
|
|
int memberIdx = 0;
|
|
|
|
tupleType = tupleType->RemoveModifiers();
|
|
tupleType = tupleType->GetPrimaryType();
|
|
|
|
for (auto member : tupleType->mMemberList)
|
|
{
|
|
if (member->mIsStatic)
|
|
continue;
|
|
|
|
DbgTypedValue receivingValue;
|
|
receivingValue.mSrcAddress = receiveAddr + member->mMemberOffset;
|
|
receivingValue.mType = member->mType;
|
|
|
|
if (memberIdx < argValues.size())
|
|
{
|
|
if ((names != NULL) && (memberIdx < names->size()) && ((*names)[memberIdx] != NULL))
|
|
{
|
|
String name;
|
|
if (auto tupleName = BfNodeDynCast<BfTupleNameNode>((*names)[memberIdx]))
|
|
name = tupleName->mNameNode->ToString();
|
|
else
|
|
name = (*names)[memberIdx]->ToString();
|
|
if ((member->mName != NULL) && (!isdigit(member->mName[0])) && (name != member->mName))
|
|
{
|
|
Fail(StrFormat("Name '%s' does not match expect name '%s'", name.c_str(), member->mName), (*names)[memberIdx]);
|
|
}
|
|
}
|
|
|
|
StoreValue(receivingValue, argValues[memberIdx]);
|
|
}
|
|
else
|
|
{
|
|
Fail("Not enough arguments", targetSrc);
|
|
}
|
|
|
|
memberIdx++;
|
|
}
|
|
|
|
if (memberIdx < argValues.size())
|
|
{
|
|
Fail("Too many arguments", argValues[memberIdx]);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::CheckEnumCreation(BfAstNode* targetSrc, DbgType* enumType, const StringImpl& caseName, const BfSizedArray<BfExpression*>& argValues)
|
|
{
|
|
BF_ASSERT(enumType->IsBfPayloadEnum());
|
|
|
|
addr_target receiveAddr = 0;
|
|
if (mReceivingValue != NULL)
|
|
{
|
|
receiveAddr = mReceivingValue->mSrcAddress;
|
|
}
|
|
|
|
if (receiveAddr == 0)
|
|
{
|
|
Fail("Address for enum cannot be inferred", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
enumType = enumType->RemoveModifiers();
|
|
enumType = enumType->GetPrimaryType();
|
|
|
|
int caseNum = -1;
|
|
DbgVariable* matchedMember = NULL;
|
|
for (auto member : enumType->mMemberList)
|
|
{
|
|
if ((member->mName[0] == '_') && (member->mName[1] >= '0') && (member->mName[1] <= '9'))
|
|
{
|
|
for (int i = 1; true; i++)
|
|
{
|
|
if (member->mName[i] == '_')
|
|
{
|
|
if (caseName == member->mName + i + 1)
|
|
{
|
|
caseNum = atoi(member->mName + 1);
|
|
matchedMember = member;
|
|
}
|
|
break;
|
|
}
|
|
else if (member->mName[i] == 0)
|
|
break;
|
|
}
|
|
}
|
|
if (strcmp(member->mName, "__bftag") == 0)
|
|
{
|
|
if (mReceivingValue != NULL)
|
|
mReceivingValue = NULL;
|
|
|
|
mDebugger->WriteMemory(receiveAddr + member->mMemberOffset, &caseNum, member->mType->GetByteCount());
|
|
CheckTupleCreation(receiveAddr + matchedMember->mMemberOffset, targetSrc, matchedMember->mType, argValues, NULL);
|
|
|
|
DbgTypedValue typedValue;
|
|
typedValue.mType = enumType;
|
|
typedValue.mSrcAddress = receiveAddr;
|
|
return typedValue;
|
|
}
|
|
}
|
|
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
bool DbgExprEvaluator::IsAutoCompleteNode(BfAstNode* node, int lengthAdd)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
return ((mCursorPos >= node->GetSrcStart()) && (mCursorPos < node->GetSrcEnd() + lengthAdd));
|
|
}
|
|
|
|
void DbgExprEvaluator::AutocompleteCheckType(BfTypeReference* typeReference)
|
|
{
|
|
if (!IsAutoCompleteNode(typeReference))
|
|
return;
|
|
|
|
if (auto namedTypeRef = BfNodeDynCast <BfNamedTypeReference>(typeReference))
|
|
{
|
|
String filter = namedTypeRef->ToString();
|
|
mAutoComplete->mInsertStartIdx = namedTypeRef->GetSrcStart();
|
|
mAutoComplete->mInsertEndIdx = namedTypeRef->GetSrcEnd();
|
|
|
|
AutocompleteAddTopLevelTypes(filter);
|
|
}
|
|
else if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeReference))
|
|
{
|
|
if (IsAutoCompleteNode(qualifiedTypeRef->mLeft))
|
|
{
|
|
AutocompleteCheckType(qualifiedTypeRef->mLeft);
|
|
return;
|
|
}
|
|
|
|
auto dbgType = ResolveTypeRef(qualifiedTypeRef->mLeft);
|
|
if (dbgType != NULL)
|
|
{
|
|
String filter;
|
|
if (qualifiedTypeRef->mRight != NULL)
|
|
{
|
|
filter = qualifiedTypeRef->mRight->ToString();
|
|
mAutoComplete->mInsertStartIdx = qualifiedTypeRef->mRight->GetSrcStart();
|
|
mAutoComplete->mInsertEndIdx = qualifiedTypeRef->mRight->GetSrcEnd();
|
|
}
|
|
else
|
|
{
|
|
mAutoComplete->mInsertStartIdx = qualifiedTypeRef->mDot->GetSrcEnd();
|
|
mAutoComplete->mInsertEndIdx = qualifiedTypeRef->mDot->GetSrcEnd();
|
|
}
|
|
AutocompleteAddMembers(dbgType, false, false, filter);
|
|
}
|
|
}
|
|
else if (auto elementedTypeRef = BfNodeDynCast<BfElementedTypeRef>(typeReference))
|
|
{
|
|
AutocompleteCheckType(elementedTypeRef->mElementType);
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::AutocompleteAddTopLevelTypes(const StringImpl& filter)
|
|
{
|
|
GetNamespaceSearch();
|
|
for (auto usingNamespace : mNamespaceSearch)
|
|
{
|
|
usingNamespace = usingNamespace->GetPrimaryType();
|
|
AutocompleteAddMembers(usingNamespace, true, false, filter);
|
|
}
|
|
|
|
mDbgModule->ParseGlobalsData();
|
|
int methodEntryCount = 0;
|
|
//for (auto compileUnit : mDbgModule->mCompileUnits)
|
|
for (int compileIdx = 0; compileIdx < (int)mDbgModule->mCompileUnits.size(); compileIdx++)
|
|
{
|
|
auto compileUnit = mDbgModule->mCompileUnits[compileIdx];
|
|
if (mDbgCompileUnit == NULL)
|
|
continue;
|
|
if ((compileUnit->mLanguage != DbgLanguage_Unknown) && (compileUnit->mLanguage != mDbgCompileUnit->mLanguage))
|
|
continue;
|
|
|
|
AutocompleteAddMembers(compileUnit->mGlobalType, true, false, filter);
|
|
|
|
/*for (auto dwMethod : compileUnit->mGlobalType.mMethodList)
|
|
{
|
|
if (dwMethod->mName != NULL)
|
|
{
|
|
methodEntryCount++;
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("method", dwMethod->mName), filter);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
auto language = GetLanguage();
|
|
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
for (auto primType : mDbgModule->mBfPrimitiveTypes)
|
|
{
|
|
if (primType != NULL)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primType->mName), filter);
|
|
}
|
|
|
|
char* primNames[2] = { "int", "uint" };
|
|
for (auto primName : primNames)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primName), filter);
|
|
}
|
|
else
|
|
{
|
|
for (auto primType : mDbgModule->mCPrimitiveTypes)
|
|
{
|
|
if (primType != NULL)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primType->mName), filter);
|
|
}
|
|
}
|
|
|
|
int typeEntrCount = 0;
|
|
|
|
//TODO: Does this get everything?
|
|
/*for (auto dwTypeEntry : mDbgModule->mTypeMap)
|
|
{
|
|
auto dbgType = dwTypeEntry->mValue;
|
|
if ((dbgType->mParent == NULL) && (dbgType->mTypeParam == NULL) &&
|
|
(dbgType->mTypeCode != DbgType_Ptr) && (dbgType->mTypeCode != DbgType_Ref))
|
|
{
|
|
String typeStr = dbgType->ToString();
|
|
if (typeStr[typeStr.length() - 1] == '^')
|
|
continue;
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("type", typeStr), filter);
|
|
typeEntrCount++;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::LookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError)
|
|
{
|
|
if (!mDebugger->mIsRunning)
|
|
return DbgTypedValue();
|
|
|
|
auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(identifierNode);
|
|
if (qualifiedNameNode != NULL)
|
|
{
|
|
LookupQualifiedName(qualifiedNameNode, ignoreInitialError, hadError);
|
|
auto qualifiedResult = mResult;
|
|
mResult = DbgTypedValue();
|
|
return qualifiedResult;
|
|
}
|
|
|
|
String findName = identifierNode->ToString();
|
|
if ((findName.StartsWith('$')) && (findName != "$prim"))
|
|
{
|
|
if (IsAutoCompleteNode(identifierNode))
|
|
{
|
|
String filter = identifierNode->ToString();
|
|
|
|
const char* identifiers[] =
|
|
{
|
|
"$ThreadId", "$ThreadName", "$TargetName", "$TargetPath", "$ModuleName", "$ModulePath",
|
|
"$HitCount", "$ProcessId"
|
|
};
|
|
for (int idx = 0; idx < BF_ARRAY_COUNT(identifiers); idx++)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("dbg", identifiers[idx]), filter);
|
|
|
|
mAutoComplete->mInsertStartIdx = identifierNode->GetSrcStart();
|
|
mAutoComplete->mInsertEndIdx = identifierNode->GetSrcEnd();
|
|
}
|
|
|
|
if (findName == "$this")
|
|
return GetThis();
|
|
else if (findName == "$ThreadId")
|
|
return GetInt(mDebugger->mActiveThread->mThreadId);
|
|
else if (findName == "$ThreadName")
|
|
return GetString(mDebugger->mActiveThread->mName);
|
|
else if (findName == "$TargetName")
|
|
return GetString(GetFileName(mDebugTarget->mTargetPath));
|
|
else if (findName == "LaunchName")
|
|
return GetString(GetFileName(mDebugTarget->mLaunchBinary->mFilePath));
|
|
else if (findName == "$TargetPath")
|
|
return GetString(mDebugTarget->mTargetPath);
|
|
else if (findName == "$ModuleName")
|
|
return GetString(GetFileName(mDbgModule->mFilePath));
|
|
else if (findName == "$ModulePath")
|
|
return GetString(mDbgModule->mFilePath);
|
|
else if (findName == "$HitCount")
|
|
{
|
|
if (mDebugger->mActiveBreakpoint != NULL)
|
|
return GetInt(mDebugger->mActiveBreakpoint->GetHeadBreakpoint()->mHitCount);
|
|
return GetInt(0);
|
|
}
|
|
else if (findName == "$BreakpointCondition")
|
|
{
|
|
if ((mDebugger->mActiveBreakpoint != NULL) && (mDebugger->mActiveBreakpoint->mCondition != NULL))
|
|
return GetString(mDebugger->mActiveBreakpoint->GetHeadBreakpoint()->mCondition->mExpr);
|
|
return GetString("");
|
|
}
|
|
else if (findName == "$ProcessId")
|
|
return GetInt(mDebugger->mProcessInfo.dwProcessId);
|
|
|
|
bool mayBeRegister = true;
|
|
for (int i = 1; i < findName.length(); i++)
|
|
if (findName[i] == '$')
|
|
mayBeRegister = false;
|
|
if (mayBeRegister)
|
|
{
|
|
DbgTypedValue val = GetRegister(findName.Substring(1));
|
|
if (val)
|
|
return val;
|
|
}
|
|
}
|
|
|
|
DbgTypedValue result;
|
|
if (mExplicitThis)
|
|
{
|
|
if ((mExplicitThis.mSrcAddress == -1) && ((DbgVariable*)mExplicitThis.mVariable != NULL))
|
|
{
|
|
mResult = DbgTypedValue();
|
|
LookupSplatMember(mExplicitThis, findName);
|
|
result = mResult;
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else if (!result)
|
|
{
|
|
mExplicitThis.mType = mExplicitThis.mType->RemoveModifiers();
|
|
result = LookupField(identifierNode, mExplicitThis, findName);
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
if (mExplicitThisExpr != NULL)
|
|
mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(identifierNode, mCurChildRef));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
CPUStackFrame* stackFrame = GetStackFrame();
|
|
auto language = GetLanguage();
|
|
|
|
intptr valAddr;
|
|
DbgType* valType;
|
|
DbgAddrType addrType = DbgAddrType_None;
|
|
|
|
if (IsAutoCompleteNode(identifierNode))
|
|
{
|
|
String filter = identifierNode->ToString();
|
|
|
|
DbgType* dbgType = NULL;
|
|
|
|
auto currentMethod = GetCurrentMethod();
|
|
auto thisVal = GetThis();
|
|
auto targetType = thisVal.mType;
|
|
if (currentMethod != NULL)
|
|
{
|
|
if (targetType != NULL)
|
|
dbgType = targetType;
|
|
else if (!thisVal)
|
|
dbgType = currentMethod->GetTargetType();
|
|
}
|
|
|
|
Array<String> capturedNames;
|
|
Array<DbgType*> capturedTypes;
|
|
mDebugTarget->mCapturedNamesPtr = &capturedNames;
|
|
mDebugTarget->mCapturedTypesPtr = &capturedTypes;
|
|
{
|
|
mDebugTarget->GetValueByName(GetCurrentMethod(), "*", stackFrame, &valAddr, &valType, &addrType);
|
|
}
|
|
mDebugTarget->mCapturedNamesPtr = NULL;
|
|
mDebugTarget->mCapturedTypesPtr = NULL;
|
|
auto language = GetLanguage();
|
|
BF_ASSERT(capturedTypes.size() == capturedNames.size());
|
|
{
|
|
//for (auto capturedName : capturedNames)
|
|
for (int i = 0; i < (int)capturedNames.size(); i++)
|
|
{
|
|
const String capturedName = capturedNames[i];
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
// Don't put splats
|
|
if ((capturedName.length() > 0) && (capturedName[0] == '$'))
|
|
continue;
|
|
}
|
|
//TODO: Show the type icon
|
|
mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(capturedTypes[i]), capturedNames[i]), filter);
|
|
}
|
|
}
|
|
|
|
if (!mSubjectExpr.IsEmpty())
|
|
{
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", "_"), filter);
|
|
}
|
|
|
|
if (dbgType != NULL)
|
|
{
|
|
dbgType = dbgType->RemoveModifiers();
|
|
if (dbgType->IsPointerOrRef())
|
|
{
|
|
dbgType = dbgType->mTypeParam;
|
|
dbgType = dbgType->RemoveModifiers();
|
|
}
|
|
if (dbgType->mIsDeclaration)
|
|
dbgType = dbgType->GetPrimaryType();
|
|
|
|
bool wantsStatic = true;
|
|
bool wantsNonStatic = currentMethod->mHasThis;
|
|
// In Beef, we can only access statics by class name
|
|
if (dbgType->GetLanguage() != DbgLanguage_Beef)
|
|
wantsStatic = !wantsNonStatic;
|
|
AutocompleteAddMembers(dbgType, wantsStatic, wantsNonStatic, filter);
|
|
if (currentMethod != NULL)
|
|
{
|
|
if (language == DbgLanguage_C)
|
|
{
|
|
// C++ closures
|
|
auto currentMethod = GetCurrentMethod();
|
|
if (currentMethod != NULL)
|
|
{
|
|
if (strstr(currentMethod->mName, "operator()") != NULL)
|
|
{
|
|
if (mDebugTarget->GetValueByName(currentMethod, "this", stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
valType = valType->RemoveModifiers();
|
|
if (valType->IsPointer())
|
|
valType = valType->mTypeParam;
|
|
valType = valType->RemoveModifiers();
|
|
AutocompleteAddMembers(valType, true, true, filter, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If in a Beef closure...
|
|
if (mDebugTarget->GetValueByName(currentMethod, "__closure", stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
valType = valType->RemoveModifiers();
|
|
if (valType->IsPointer())
|
|
valType = valType->mTypeParam;
|
|
AutocompleteAddMembers(valType, true, true, filter, true);
|
|
}
|
|
}
|
|
|
|
auto targetType = currentMethod->GetTargetType();
|
|
if ((targetType != dbgType) && (targetType != NULL))
|
|
{
|
|
AutocompleteAddMembers(targetType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
mAutoComplete->mInsertStartIdx = identifierNode->GetSrcStart();
|
|
mAutoComplete->mInsertEndIdx = identifierNode->GetSrcEnd();
|
|
|
|
AutocompleteAddTopLevelTypes(filter);
|
|
}
|
|
|
|
if (language == DbgLanguage_C)
|
|
{
|
|
// For C++ lambdas, captured a "this" is named "__this", so allow lookups into that
|
|
auto currentMethod = GetCurrentMethod();
|
|
if (currentMethod != NULL)
|
|
{
|
|
if (strstr(currentMethod->mName, "operator()") != NULL)
|
|
{
|
|
DbgTypedValue rootThisValue = GetThis();
|
|
if (rootThisValue)
|
|
{
|
|
DbgTypedValue thisValue = LookupField(identifierNode, rootThisValue, "__this");
|
|
if (thisValue)
|
|
{
|
|
if (findName == "this")
|
|
return thisValue;
|
|
|
|
auto fieldValue = LookupField(identifierNode, thisValue, findName);
|
|
if (fieldValue)
|
|
return fieldValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (findName == "this")
|
|
return GetThis();
|
|
if (findName == "_")
|
|
{
|
|
if (mSubjectValue)
|
|
{
|
|
if (mSubjectValue.mSrcAddress != 0)
|
|
{
|
|
auto refreshVal = ReadTypedValue(identifierNode, mSubjectValue.mType, mSubjectValue.mSrcAddress, DbgAddrType_Target);
|
|
if (refreshVal)
|
|
mSubjectValue = refreshVal;
|
|
}
|
|
return mSubjectValue;
|
|
}
|
|
|
|
if (!mSubjectExpr.IsEmpty())
|
|
{
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mLanguage = language;
|
|
DbgEvaluationContext dbgEvaluationContext(mDebugger, mDbgModule, mSubjectExpr, &formatInfo);
|
|
mSubjectValue = dbgEvaluationContext.EvaluateInContext(mExplicitThis);
|
|
if (!mSubjectValue)
|
|
{
|
|
Fail("Failed to generate subject value", identifierNode);
|
|
}
|
|
|
|
if (mSubjectValue)
|
|
return mSubjectValue;
|
|
}
|
|
}
|
|
|
|
if (stackFrame != NULL)
|
|
{
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
//BF_ASSERT(valType != NULL);
|
|
|
|
if (valType == NULL)
|
|
{
|
|
if (sizeof(addr_target) == 8)
|
|
valType = mDbgModule->GetPrimitiveType(DbgType_i64, DbgLanguage_C);
|
|
else
|
|
valType = mDbgModule->GetPrimitiveType(DbgType_i32, DbgLanguage_C);
|
|
}
|
|
|
|
if (valType != NULL)
|
|
{
|
|
if (mReferenceId != NULL)
|
|
*mReferenceId = findName;
|
|
|
|
//bool isLocal = !valIsAddr;
|
|
//if (valType->IsCompositeType())
|
|
//isLocal = false;
|
|
return ReadTypedValue(identifierNode, valType, valAddr, addrType);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isClosure = false;
|
|
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
intptr valAddr;
|
|
DbgType* valType;
|
|
//bool valIsAddr = false;
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
DbgTypedValue closureValue = ReadTypedValue(identifierNode, valType, valAddr, addrType);
|
|
if (closureValue)
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
DbgTypedValue fieldValue = LookupField(identifierNode, closureValue, findName);
|
|
if (fieldValue)
|
|
return fieldValue;
|
|
|
|
DbgTypedValue thisValue = LookupField(identifierNode, closureValue, "__this");
|
|
if (thisValue)
|
|
{
|
|
fieldValue = LookupField(identifierNode, thisValue, findName);
|
|
if (fieldValue)
|
|
return fieldValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*if (mModule->mCurMethodState != NULL)
|
|
{
|
|
for (int localIdx = mModule->mCurMethodState->mLocals.size() - 1; localIdx >= 0; localIdx--)
|
|
{
|
|
auto& varDecl = mModule->mCurMethodState->mLocals[localIdx];
|
|
if (varDecl.mName == findName)
|
|
{
|
|
if (varDecl.mAddr != NULL)
|
|
{
|
|
return DbgTypedValue(varDecl.mAddr, varDecl.mUnresolvedType, true);
|
|
}
|
|
else
|
|
return DbgTypedValue(varDecl.mArgument, varDecl.mUnresolvedType, false);
|
|
return DbgTypedValue();
|
|
}
|
|
}
|
|
}*/
|
|
|
|
if (!isClosure)
|
|
{
|
|
// This uses an incorrect 'this' in the case of a closure
|
|
result = LookupField(identifierNode, DbgTypedValue(), findName);
|
|
if ((result) || (HasPropResult()))
|
|
return result;
|
|
}
|
|
|
|
result = GetRegister(findName);
|
|
if ((result) || (HasPropResult()))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
mDbgModule->ParseGlobalsData();
|
|
for (auto compileUnit : mDbgModule->mCompileUnits)
|
|
{
|
|
if (mDbgCompileUnit == NULL)
|
|
continue;
|
|
if ((compileUnit->mLanguage != DbgLanguage_Unknown) && (compileUnit->mLanguage != mDbgCompileUnit->mLanguage))
|
|
continue;
|
|
|
|
result = DoLookupField(identifierNode, DbgTypedValue(), compileUnit->mGlobalType, findName, NULL, false);
|
|
if ((result) || (HasPropResult()))
|
|
return result;
|
|
}
|
|
|
|
auto currentMethod = GetCurrentMethod();
|
|
if ((currentMethod != NULL) && (currentMethod->mParentType == NULL) && (currentMethod->mHasQualifiedName))
|
|
{
|
|
currentMethod->mCompileUnit->mDbgModule->MapCompileUnitMethods(currentMethod->mCompileUnit);
|
|
}
|
|
|
|
if ((currentMethod != NULL) && (currentMethod->mParentType != NULL))
|
|
{
|
|
auto dbgType = currentMethod->mParentType;
|
|
if (dbgType->IsPointerOrRef())
|
|
dbgType = dbgType->mTypeParam;
|
|
|
|
auto curAlloc = &dbgType->mCompileUnit->mDbgModule->mAlloc;
|
|
|
|
result = DoLookupField(identifierNode, DbgTypedValue(), dbgType, findName, NULL, false);
|
|
if ((result) || (HasPropResult()))
|
|
return result;
|
|
|
|
GetNamespaceSearch();
|
|
for (auto usingNamespace : mNamespaceSearch)
|
|
{
|
|
usingNamespace = usingNamespace->GetPrimaryType();
|
|
result = DoLookupField(identifierNode, DbgTypedValue(), usingNamespace, findName, NULL, false);
|
|
if ((result) || (HasPropResult()))
|
|
return result;
|
|
|
|
for (auto checkNamespace : usingNamespace->mAlternates)
|
|
{
|
|
result = DoLookupField(identifierNode, DbgTypedValue(), checkNamespace, findName, NULL, false);
|
|
if ((result) || (HasPropResult()))
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfAssignmentExpression* assignExpr)
|
|
{
|
|
mHadSideEffects = true;
|
|
|
|
auto binaryOp = BfAssignOpToBinaryOp(assignExpr->mOp);
|
|
|
|
//auto ptr = mModule->GetOrCreateVarAddr(assignExpr->mLeft);
|
|
VisitChild(assignExpr->mLeft);
|
|
if ((!mResult) && (!HasPropResult()))
|
|
return;
|
|
|
|
if (HasPropResult())
|
|
{
|
|
if (mPropSet == NULL)
|
|
{
|
|
Fail("Property has no setter", mPropSrc);
|
|
return;
|
|
}
|
|
|
|
auto propSrc = mPropSrc;
|
|
auto propSet = mPropSet;
|
|
auto propTarget = mPropTarget;
|
|
auto indexerValues = mIndexerValues;
|
|
|
|
auto indexerExprValues = mIndexerExprValues;
|
|
mPropGet = NULL;
|
|
mPropSet = NULL;
|
|
mPropSrc = NULL;
|
|
mPropTarget = DbgTypedValue();
|
|
mIndexerValues.clear();
|
|
mIndexerExprValues.clear();
|
|
|
|
auto valueParam = propSet->mParams.back();
|
|
if (valueParam == NULL)
|
|
{
|
|
Fail("Invalid property setter", mPropSrc);
|
|
return;
|
|
}
|
|
|
|
DbgTypedValue convVal;
|
|
if (binaryOp != BfBinaryOp_None)
|
|
{
|
|
PerformBinaryOperation(assignExpr->mLeft, assignExpr->mRight, binaryOp, assignExpr->mOpToken, true);
|
|
if (!mResult)
|
|
return;
|
|
convVal = mResult;
|
|
}
|
|
else
|
|
{
|
|
convVal = CreateValueFromExpression(assignExpr->mRight, valueParam->mType);
|
|
}
|
|
if (!convVal)
|
|
return;
|
|
|
|
|
|
// SizedArray<DbgTypedValue, 4> argPushQueue;
|
|
// if (propSet->mHasThis)
|
|
// argPushQueue.push_back(propTarget);
|
|
// for (auto indexer : indexerValues)
|
|
// argPushQueue.push_back(indexer);
|
|
// argPushQueue.push_back(convVal);
|
|
// if (propSet->mParams.Size() == argPushQueue.size())
|
|
// {
|
|
// mResult = CreateCall(propSet, argPushQueue, false);
|
|
//
|
|
// }
|
|
|
|
indexerExprValues.push_back(assignExpr->mRight);
|
|
indexerValues.push_back(convVal);
|
|
mResult = CreateCall(propSrc, propTarget, propSet, false, indexerExprValues, indexerValues);
|
|
|
|
return;
|
|
}
|
|
|
|
GetResult();
|
|
auto ptr = mResult;
|
|
mResult = DbgTypedValue();
|
|
|
|
if (binaryOp != NULL)
|
|
{
|
|
PerformBinaryOperation(assignExpr->mLeft, assignExpr->mRight, binaryOp, assignExpr->mOpToken, true);
|
|
}
|
|
else
|
|
{
|
|
SetAndRestoreValue<DbgTypedValue*> prevReceiveValue(mReceivingValue, &ptr);
|
|
|
|
mResult = DbgTypedValue();
|
|
mResult = CreateValueFromExpression(assignExpr->mRight, ptr.mType, DbgEvalExprFlags_NoCast);
|
|
|
|
if (mReceivingValue == NULL)
|
|
{
|
|
// Receiving value used
|
|
mResult = ptr;
|
|
return;
|
|
}
|
|
}
|
|
if (!mResult)
|
|
return;
|
|
|
|
if ((ptr.mType->mTypeCode == DbgType_Ref) || (ptr.mType->mTypeCode == DbgType_RValueReference))
|
|
ptr.mType = ptr.mType->mTypeParam;
|
|
|
|
if ((mResult.mType->mTypeCode == DbgType_i32) && (mResult.mIsLiteral) && (ptr.mType->IsPointer()))
|
|
{
|
|
// Allow for literal pointer setting
|
|
mResult.mType = ptr.mType;
|
|
}
|
|
|
|
if ((mResult.mType->IsPointer()) && (mResult.mIsLiteral))
|
|
{
|
|
Fail("Cannot assign from literal value", assignExpr->mRight);
|
|
return;
|
|
}
|
|
|
|
mResult = Cast(assignExpr->mRight, mResult, ptr.mType, true);
|
|
if (!mResult)
|
|
return;
|
|
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowSideEffects) == 0)
|
|
{
|
|
mBlockedSideEffects = true;
|
|
return;
|
|
}
|
|
|
|
StoreValue(ptr, mResult, assignExpr->mLeft);
|
|
}
|
|
|
|
bool DbgExprEvaluator::StoreValue(DbgTypedValue& ptr, DbgTypedValue& value, BfAstNode* refNode)
|
|
{
|
|
if ((!HasPropResult()) && ((ptr.mSrcAddress == 0) || (ptr.mIsReadOnly)) && (ptr.mRegNum < 0))
|
|
{
|
|
Fail("Cannot assign to value", refNode);
|
|
return false;
|
|
}
|
|
|
|
if (ptr.mSrcAddress)
|
|
{
|
|
if (ptr.mType->mTypeCode == DbgType_Bitfield)
|
|
{
|
|
auto dbgBitfieldType = (DbgBitfieldType*)ptr.mType;
|
|
|
|
uint64 tempVal = 0;
|
|
mDebugger->ReadMemory(ptr.mSrcAddress, ptr.mType->GetByteCount(), &tempVal);
|
|
|
|
uint64 srcMask = ((uint64)1 << dbgBitfieldType->mLength) - 1;
|
|
uint64 destMask = srcMask << dbgBitfieldType->mPosition;
|
|
value.mUInt64 &= srcMask;
|
|
tempVal &= ~destMask;
|
|
tempVal |= value.mUInt64 << dbgBitfieldType->mPosition;
|
|
|
|
if (!mDebugger->WriteMemory(ptr.mSrcAddress, &tempVal, ptr.mType->GetByteCount()))
|
|
mPassInstance->Fail("Failed to write to memory");
|
|
|
|
if ((dbgBitfieldType->mTypeParam->IsSigned()) && ((value.mUInt64 & (1LL << (dbgBitfieldType->mLength - 1))) != 0))
|
|
{
|
|
// Sign extend
|
|
value.mUInt64 |= ~srcMask;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!mDebugger->WriteMemory(ptr.mSrcAddress, &value.mInt8, ptr.mType->GetByteCount()))
|
|
mPassInstance->Fail("Failed to write to memory");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
String error;
|
|
if (!mDebugger->AssignToReg(mCallStackIdx, ptr, value, error))
|
|
mPassInstance->Fail(error);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DbgExprEvaluator::StoreValue(DbgTypedValue& ptr, BfExpression* expr)
|
|
{
|
|
auto receiveAddr = ptr.mSrcAddress;
|
|
if (receiveAddr == 0)
|
|
{
|
|
Fail("Unable to determine receiving address", expr);
|
|
return false;
|
|
}
|
|
|
|
SetAndRestoreValue<DbgTypedValue*> prevReceivingValue(mReceivingValue, &ptr);
|
|
|
|
auto wantType = ptr.mType->RemoveModifiers();
|
|
auto result = Resolve(expr, ptr.mType);
|
|
|
|
if (mReceivingValue == NULL)
|
|
return true; // Already written
|
|
if (!result)
|
|
return false;
|
|
|
|
return StoreValue(ptr, result, expr);;
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfParenthesizedExpression* parenExpr)
|
|
{
|
|
VisitChild(parenExpr->mExpression);
|
|
}
|
|
|
|
const char* DbgExprEvaluator::GetTypeName(DbgType* type)
|
|
{
|
|
if (type != NULL)
|
|
{
|
|
if (type->IsBfObjectPtr())
|
|
return "object";
|
|
if (type->IsPointer())
|
|
return "pointer";
|
|
}
|
|
return "value";
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::GetResult()
|
|
{
|
|
if (!mResult)
|
|
{
|
|
if (HasPropResult())
|
|
{
|
|
if (mPropGet == NULL)
|
|
{
|
|
Fail("Property has no getter", mPropSrc);
|
|
}
|
|
else
|
|
{
|
|
// SizedArray<DbgTypedValue, 4> argPushQueue;
|
|
// auto curParam = mPropGet->mParams.mHead;
|
|
// if (mPropGet->mHasThis)
|
|
// {
|
|
// argPushQueue.push_back(mPropTarget);
|
|
// if (curParam != NULL)
|
|
// curParam = curParam->mNext;
|
|
// }
|
|
// bool failed = false;
|
|
// for (int indexerIdx = 0; indexerIdx < (int)mIndexerValues.size(); indexerIdx++)
|
|
// {
|
|
// auto val = mIndexerValues[indexerIdx];
|
|
// if (curParam != NULL)
|
|
// {
|
|
// val = Cast(mPropSrc, val, curParam->mType);
|
|
// if (!val)
|
|
// {
|
|
// failed = true;
|
|
// break;
|
|
// }
|
|
// }
|
|
// argPushQueue.push_back(val);
|
|
//
|
|
// if (curParam != NULL)
|
|
// curParam = curParam->mNext;
|
|
// }
|
|
// if (!failed)
|
|
// {
|
|
// if (mPropGet->mParams.Size() == argPushQueue.size())
|
|
// {
|
|
// mResult = CreateCall(mPropGet, argPushQueue, false);
|
|
// }
|
|
// else
|
|
// {
|
|
// Fail("Indexer parameter count mismatch", mPropSrc);
|
|
// }
|
|
// }
|
|
|
|
SetAndRestoreValue<DwEvalExpressionFlags> prevFlags(mExpressionFlags);
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowPropertyEval) != 0)
|
|
mExpressionFlags = (DwEvalExpressionFlags)(mExpressionFlags | DwEvalExpressionFlag_AllowCalls);
|
|
mResult = CreateCall(mPropSrc, mPropTarget, mPropGet, false, mIndexerExprValues, mIndexerValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((mResult) && (mResult.mType->IsConst()) && (GetLanguage() == DbgLanguage_Beef))
|
|
{
|
|
mResult.mIsReadOnly = true; // Pretend we don't really have the address
|
|
}
|
|
|
|
mPropGet = NULL;
|
|
mPropSet = NULL;
|
|
mPropSrc = NULL;
|
|
mIndexerValues.clear();
|
|
mIndexerExprValues.clear();
|
|
|
|
return mResult;
|
|
}
|
|
|
|
bool DbgExprEvaluator::HasPropResult()
|
|
{
|
|
return (mPropGet != NULL) || (mPropSet != NULL);
|
|
}
|
|
|
|
DbgLanguage DbgExprEvaluator::GetLanguage()
|
|
{
|
|
if (mLanguage != DbgLanguage_NotSet)
|
|
return mLanguage;
|
|
if ((mDbgCompileUnit != NULL) && (mDbgCompileUnit->mLanguage != DbgLanguage_Unknown))
|
|
return mDbgCompileUnit->mLanguage;
|
|
if (mExplicitThis)
|
|
return mExplicitThis.mType->mLanguage;
|
|
auto currentMethod = GetCurrentMethod();
|
|
if (currentMethod != NULL)
|
|
return currentMethod->GetLanguage();
|
|
return DbgLanguage_Beef; // Default to Beef
|
|
}
|
|
|
|
DbgFlavor DbgExprEvaluator::GetFlavor()
|
|
{
|
|
if (mDbgCompileUnit != NULL)
|
|
return mDbgCompileUnit->mDbgModule->mDbgFlavor;
|
|
if (mExplicitThis)
|
|
return mExplicitThis.mType->mCompileUnit->mDbgModule->mDbgFlavor;
|
|
return DbgFlavor_Unknown;
|
|
}
|
|
|
|
void DbgExprEvaluator::AutocompleteAddMethod(const char* methodName, const StringImpl& filter)
|
|
{
|
|
const char* atPos = strchr(methodName, '@');
|
|
if (atPos == NULL)
|
|
{
|
|
if ((strncmp(methodName, "get__", 5) == 0) ||
|
|
(strncmp(methodName, "set__", 5) == 0))
|
|
{
|
|
const char* propName = methodName + 5;
|
|
if (*propName != (char)0)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("property", propName), filter);
|
|
}
|
|
else if (strncmp(methodName, "__", 2) != 0)
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("method", methodName), filter);
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::AutocompleteAddMembers(DbgType* dbgType, bool wantsStatic, bool wantsNonStatic, const StringImpl& filter, bool isCapture)
|
|
{
|
|
DbgLanguage language = GetLanguage();
|
|
DbgFlavor flavor = GetFlavor();
|
|
if ((mDbgCompileUnit != NULL) && (dbgType->mLanguage != DbgLanguage_Unknown) && (dbgType->mLanguage != language))
|
|
return;
|
|
|
|
dbgType->PopulateType();
|
|
if (dbgType->mNeedsGlobalsPopulated)
|
|
mDbgModule->PopulateTypeGlobals(dbgType);
|
|
|
|
// Don't get primary type for namespace, causes mAlternates infinite loop
|
|
if (!dbgType->IsNamespace())
|
|
dbgType = dbgType->GetPrimaryType();
|
|
|
|
// false/false means just add subtypes
|
|
if ((wantsStatic) || (!wantsNonStatic))
|
|
{
|
|
auto subType = dbgType->mSubTypeList.mHead;
|
|
while (subType != NULL)
|
|
{
|
|
if (subType->mLanguage != language)
|
|
{
|
|
// Ignore
|
|
}
|
|
else if (subType->mTypeName == NULL)
|
|
{
|
|
AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
else
|
|
{
|
|
bool allowType = true;
|
|
for (const char* cPtr = subType->mTypeName; *cPtr != '\0'; cPtr++)
|
|
{
|
|
char c = *cPtr;
|
|
if ((c == '`') || (c == '[') || (c == '.') || (c == '$') || (c == '('))
|
|
allowType = false;
|
|
}
|
|
|
|
if ((allowType) && (subType->IsNamespace()))
|
|
{
|
|
// Is it a "using" declaration?
|
|
if ((!dbgType->IsRoot()) && (!dbgType->IsNamespace()))
|
|
allowType = false;
|
|
}
|
|
|
|
if ((!subType->IsAnonymous()) && (!subType->IsSizedArray()) && (allowType))
|
|
{
|
|
mDbgModule->TempRemoveTemplateStr(subType->mTypeName, subType->mTemplateNameIdx);
|
|
// We set the true mEntryType after the filter passes. This is because IsBfObject() is a "heavyweight" operation that we don't want to
|
|
// just run on ALL types immediately
|
|
AutoCompleteEntry* entry = mAutoComplete->AddEntry(AutoCompleteEntry("type", subType->mTypeName), filter);
|
|
if (entry != NULL)
|
|
{
|
|
if (subType->IsBfObject())
|
|
entry->mEntryType = "class";
|
|
else if (subType->IsNamespace())
|
|
entry->mEntryType = "namespace";
|
|
else
|
|
entry->mEntryType = "valuetype";
|
|
}
|
|
mDbgModule->ReplaceTemplateStr(subType->mTypeName, subType->mTemplateNameIdx);
|
|
}
|
|
else if (subType->IsGlobalsContainer())
|
|
{
|
|
if (subType->mPriority > DbgTypePriority_Normal)
|
|
AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
|
|
if ((subType->mTypeCode == DbgType_Enum) && (language == DbgLanguage_C))
|
|
{
|
|
AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
subType = subType->mNext;
|
|
}
|
|
|
|
if (dbgType->mTypeCode == DbgType_Namespace)
|
|
{
|
|
for (auto altType : dbgType->mAlternates)
|
|
{
|
|
AutocompleteAddMembers(altType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
// In beef, all globals are stored inside global containers so we can skip the rest
|
|
bool skipMembers = (language == DbgLanguage_Beef) && (dbgType->mTypeCode == DbgType_Root);
|
|
if (skipMembers)
|
|
return;
|
|
|
|
for (auto member : dbgType->mMemberList)
|
|
{
|
|
if (((member->mIsStatic) && (wantsStatic)) ||
|
|
((!member->mIsStatic) && (wantsNonStatic)))
|
|
{
|
|
const char* name = member->mName;
|
|
if (name != NULL)
|
|
{
|
|
if (member->mName[0] == '?')
|
|
continue;
|
|
|
|
mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(member->mType), name), filter);
|
|
|
|
if ((isCapture) && (strcmp(member->mName, "__this") == 0))
|
|
{
|
|
mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(member->mType), "this"), filter);
|
|
auto thisType = member->mType;
|
|
if (thisType->IsPointer())
|
|
thisType = thisType->mTypeParam;
|
|
thisType = thisType->RemoveModifiers();
|
|
AutocompleteAddMembers(thisType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AutocompleteAddMembers(member->mType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((dbgType->IsNamespace()) || (dbgType->IsRoot()))
|
|
{
|
|
// We only add these ones because we can be certain they are all static, otherwise we need the full
|
|
// method information which is in the compile units
|
|
for (auto methodNameEntry : dbgType->mMethodNameList)
|
|
{
|
|
const char* methodName = methodNameEntry->mName;
|
|
AutocompleteAddMethod(methodName, filter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//TODO: Not needed since we added the declarations now, right?
|
|
//dbgType->EnsureMethodsMapped();
|
|
}
|
|
|
|
for (auto method : dbgType->mMethodList)
|
|
{
|
|
if (((!method->mHasThis) && (wantsStatic)) ||
|
|
((method->mHasThis) && (wantsNonStatic)))
|
|
{
|
|
if ((method->mName != NULL) && (strcmp(method->mName, "this") != 0))
|
|
{
|
|
mDbgModule->FindTemplateStr(method->mName, method->mTemplateNameIdx);
|
|
if (method->mTemplateNameIdx == -1)
|
|
{
|
|
AutocompleteAddMethod(method->mName, filter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*if (!dbgType->mMethodNameList.IsEmpty())
|
|
{
|
|
BF_ASSERT(!dbgType->mMethodList.IsEmpty());
|
|
}*/
|
|
|
|
/*
|
|
for (auto methodNameEntry : dbgType->mMethodNameList)
|
|
{
|
|
const char* methodName = methodNameEntry->mName;
|
|
AutocompleteAddMethod(methodName, filter);
|
|
}*/
|
|
|
|
for (auto baseTypeEntry : dbgType->mBaseTypes)
|
|
{
|
|
AutocompleteAddMembers(baseTypeEntry->mBaseType, wantsStatic, wantsNonStatic, filter);
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::AutocompleteCheckMemberReference(BfAstNode* target, BfAstNode* dotToken, BfAstNode* memberName)
|
|
{
|
|
if ((IsAutoCompleteNode(dotToken)) || (IsAutoCompleteNode(memberName)))
|
|
{
|
|
mDbgModule->ParseGlobalsData();
|
|
bool isCType = dotToken->ToString() == "::";
|
|
|
|
String filter;
|
|
if (memberName != NULL)
|
|
{
|
|
filter = memberName->ToString();
|
|
mAutoComplete->mInsertStartIdx = memberName->GetSrcStart();
|
|
mAutoComplete->mInsertEndIdx = memberName->GetSrcEnd();
|
|
}
|
|
else
|
|
{
|
|
mAutoComplete->mInsertStartIdx = dotToken->GetSrcEnd();
|
|
mAutoComplete->mInsertEndIdx = dotToken->GetSrcEnd();
|
|
}
|
|
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
if (target == NULL)
|
|
{
|
|
mResult.mType = GetExpectingType();
|
|
if (mResult.mType != NULL)
|
|
mResult.mHasNoValue = true;
|
|
}
|
|
else if (auto typeRef = BfNodeDynCast<BfTypeReference>(target))
|
|
{
|
|
mResult.mType = ResolveTypeRef(typeRef);
|
|
mResult.mHasNoValue = true;
|
|
}
|
|
else
|
|
{
|
|
VisitChild(target);
|
|
GetResult();
|
|
}
|
|
|
|
if (mResult.mType != NULL)
|
|
{
|
|
mResult.mType = mResult.mType->RemoveModifiers();
|
|
|
|
auto dbgType = mResult.mType;
|
|
// if (dbgType->IsPrimitiveType())
|
|
// {
|
|
// //TODO: Boxed primitive structs
|
|
// }
|
|
|
|
//dbgType = dbgType->GetPrimaryType();
|
|
if ((dbgType != NULL) && (dbgType->IsPointerOrRef()))
|
|
{
|
|
dbgType = dbgType->mTypeParam;
|
|
dbgType = dbgType->RemoveModifiers();
|
|
}
|
|
//if (dbgType->mIsDeclaration)
|
|
dbgType = dbgType->GetPrimaryType();
|
|
|
|
if ((dbgType != NULL) /*&& ((dbgType->IsCompositeType()) || (dbgType->IsEnum()))*/)
|
|
{
|
|
bool wantsStatic = mResult.mHasNoValue;
|
|
bool wantsNonStatic = !mResult.mHasNoValue;
|
|
// C allows static lookups by non-static member reference
|
|
if (dbgType->GetLanguage() != DbgLanguage_Beef)
|
|
wantsStatic = true;
|
|
|
|
AutocompleteAddMembers(dbgType, wantsStatic, wantsNonStatic, filter);
|
|
|
|
if ((wantsStatic) && (dbgType->IsBfPayloadEnum()))
|
|
{
|
|
for (auto member : dbgType->mMemberList)
|
|
{
|
|
if ((member->mName[0] == '_') && (member->mName[1] >= '0') && (member->mName[1] <= '9'))
|
|
{
|
|
int val = atoi(member->mName + 1);
|
|
for (int i = 1; true; i++)
|
|
{
|
|
if (member->mName[i] == '_')
|
|
{
|
|
mAutoComplete->AddEntry(AutoCompleteEntry("method", member->mName + i + 1), filter);
|
|
break;
|
|
}
|
|
if (member->mName[i] == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//String prefixStr = dbgType->ToString();
|
|
//AutocompleteAddMembersFromNamespace(prefixStr, filter, isCType);
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else
|
|
{
|
|
//return AutocompleteAddMembersFromNamespace(memberRefExpr->mTarget->ToString(), filter, isCType);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfMemberReferenceExpression* memberRefExpr)
|
|
{
|
|
DbgTypedValue thisValue;
|
|
/*if (auto typeRef = BfNodeDynCast<DwTypeReference>(memberRefExpr->mTarget))
|
|
{
|
|
// Look up static field
|
|
//thisValue = DbgTypedValue(NULL, mModule->ResolveTypeRef(typeRef));
|
|
thisValue.mType = ResolveTypeRef(typeRef);
|
|
}*/
|
|
|
|
auto flavor = GetFlavor();
|
|
|
|
String findName;
|
|
BfAstNode* nameRefNode = memberRefExpr->mMemberName;
|
|
if (auto attrIdentifierExpr = BfNodeDynCast<BfAttributedIdentifierNode>(memberRefExpr->mMemberName))
|
|
{
|
|
nameRefNode = attrIdentifierExpr->mIdentifier;
|
|
if (nameRefNode != NULL)
|
|
findName = attrIdentifierExpr->mIdentifier->ToString();
|
|
}
|
|
else if (memberRefExpr->mMemberName != NULL)
|
|
findName = memberRefExpr->mMemberName->ToString();
|
|
|
|
AutocompleteCheckMemberReference(memberRefExpr->mTarget, memberRefExpr->mDotToken, nameRefNode);
|
|
|
|
//
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
mResult.mType = ResolveTypeRef(memberRefExpr);
|
|
if (mResult.mType != NULL)
|
|
{
|
|
mResult.mHasNoValue = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (memberRefExpr->mMemberName == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (memberRefExpr->mTarget == NULL)
|
|
{
|
|
auto expectingType = GetExpectingType();
|
|
if (expectingType != NULL)
|
|
{
|
|
DbgTypedValue expectingVal;
|
|
expectingVal.mType = expectingType;
|
|
mResult = LookupField(memberRefExpr->mMemberName, expectingVal, findName);
|
|
if ((mResult) || (HasPropResult()))
|
|
return;
|
|
}
|
|
}
|
|
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
if (memberRefExpr->mTarget == NULL)
|
|
thisValue.mType = mExpectingType;
|
|
else
|
|
thisValue.mType = ResolveTypeRef(memberRefExpr->mTarget, (BfAstNode**)&(memberRefExpr->mTarget));
|
|
if (thisValue.mType != NULL)
|
|
thisValue.mHasNoValue = true;
|
|
}
|
|
|
|
if (thisValue.mType == NULL)
|
|
{
|
|
if (auto exprTarget = BfNodeDynCast<BfExpression>(memberRefExpr->mTarget))
|
|
{
|
|
//thisValue = mModule->CreateValueFromExpression(exprTarget);
|
|
VisitChild(memberRefExpr->mTarget);
|
|
GetResult();
|
|
if (mResult.mType == NULL)
|
|
return;
|
|
mResult.mType = mResult.mType->RemoveModifiers();
|
|
thisValue = mResult;
|
|
}
|
|
else if (memberRefExpr->mTarget == NULL)
|
|
{
|
|
Fail("Identifier expected", memberRefExpr->mDotToken);
|
|
}
|
|
else
|
|
{
|
|
Fail("Unable to resolve target", memberRefExpr->mTarget);
|
|
}
|
|
}
|
|
/*else if (auto typeRef = BfNodeDynCast<BfTypeReference>(memberRefExpr->mTarget))
|
|
{
|
|
// Look up static field
|
|
thisValue.mType = ResolveTypeRef(typeRef);
|
|
}*/
|
|
|
|
if (thisValue.mSrcAddress == -1)
|
|
{
|
|
LookupSplatMember(memberRefExpr->mTarget, memberRefExpr, thisValue, findName);
|
|
}
|
|
else
|
|
mResult = LookupField(memberRefExpr->mMemberName, thisValue, findName);
|
|
/*if (mResult)
|
|
{
|
|
if (mReferenceId != NULL)
|
|
*mReferenceId = thisValue.mType->ToString() + "." + findName;
|
|
}*/
|
|
|
|
if ((!mResult) && (!HasPropResult()))
|
|
{
|
|
//String fullTokenString = memberRefExpr->ToString();
|
|
//mDbgModule->EnsureMethodsMapped(fullTokenString.c_str());
|
|
mResult = LookupField(memberRefExpr->mMemberName, thisValue, findName);
|
|
}
|
|
|
|
//TODO: Do type lookup?
|
|
|
|
if ((!mResult) && (!HasPropResult()))
|
|
{
|
|
if (thisValue.mHasNoValue)
|
|
{
|
|
for (auto altDwType : thisValue.mType->mAlternates)
|
|
{
|
|
auto altThisValue = thisValue;
|
|
altThisValue.mType = altDwType;
|
|
mResult = LookupField(memberRefExpr->mMemberName, altThisValue, findName);
|
|
if (mResult)
|
|
return;
|
|
}
|
|
}
|
|
|
|
Fail("Unable to find member", memberRefExpr->mMemberName);
|
|
}
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::RemoveRef(DbgTypedValue typedValue)
|
|
{
|
|
bool hadRef = false;
|
|
typedValue.mType = typedValue.mType->RemoveModifiers(&hadRef);
|
|
if (hadRef)
|
|
typedValue.mSrcAddress = typedValue.mPtr;
|
|
return typedValue;
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfIndexerExpression* indexerExpr)
|
|
{
|
|
VisitChild(indexerExpr->mTarget);
|
|
GetResult();
|
|
if (!mResult)
|
|
return;
|
|
DbgTypedValue collection = RemoveRef(mResult);
|
|
|
|
bool indexerFailed = false;
|
|
|
|
mIndexerValues.clear();
|
|
mIndexerExprValues.clear();
|
|
SizedArray<BfExpression*, 2> indexerExprValues;
|
|
SizedArray<DbgTypedValue, 2> indexerValues;
|
|
for (auto indexExpr : indexerExpr->mArguments)
|
|
{
|
|
indexerExprValues.push_back(indexExpr);
|
|
VisitChild(indexExpr);
|
|
GetResult();
|
|
if (!mResult)
|
|
indexerFailed = true;
|
|
indexerValues.push_back(mResult);
|
|
mResult = DbgTypedValue();
|
|
}
|
|
|
|
if (indexerFailed)
|
|
{
|
|
mResult = DbgTypedValue();
|
|
return;
|
|
}
|
|
|
|
bool isBfArrayIndex = false;
|
|
if ((collection.mType->IsBfObjectPtr()) || (collection.mType->IsStruct()))
|
|
{
|
|
DbgType* bfType = collection.mType->mTypeParam;
|
|
if (bfType != NULL)
|
|
{
|
|
DbgType* baseType = bfType->GetBaseType();
|
|
if ((baseType != NULL) && (strcmp(baseType->mName, "System.Array") == 0))
|
|
{
|
|
isBfArrayIndex = true;
|
|
}
|
|
}
|
|
|
|
if (!isBfArrayIndex)
|
|
{
|
|
mIndexerExprValues = indexerExprValues;
|
|
mIndexerValues = indexerValues;
|
|
mResult = LookupField(indexerExpr->mTarget, collection, "");
|
|
if (HasPropResult())
|
|
{
|
|
// Only use this if we actually have the method. Otherwise fall through so we can try a debug visualizer or something
|
|
if ((mPropGet != NULL) && (mPropGet->mBlock.mLowPC != 0))
|
|
return;
|
|
}
|
|
// else
|
|
// {
|
|
// mResult = DbgTypedValue();
|
|
// Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket);
|
|
// }
|
|
// return;
|
|
}
|
|
}
|
|
|
|
if (indexerValues.size() != 1)
|
|
{
|
|
Fail("Expected single index", indexerExpr->mOpenBracket);
|
|
return;
|
|
}
|
|
DbgTypedValue indexArgument = indexerValues[0];
|
|
indexArgument.mType = indexArgument.mType->RemoveModifiers();
|
|
if (!indexArgument.mType->IsInteger())
|
|
{
|
|
mResult = DbgTypedValue();
|
|
Fail("Expected integer index", indexerExpr->mArguments[0]);
|
|
return;
|
|
}
|
|
|
|
if (mReferenceId != NULL)
|
|
*mReferenceId += "[]";
|
|
|
|
if (isBfArrayIndex)
|
|
{
|
|
DbgType* bfType = collection.mType->mTypeParam;
|
|
bfType = bfType->GetPrimaryType();
|
|
|
|
DbgType* baseType = bfType->GetBaseType();
|
|
DbgVariable* lenVariable = baseType->mMemberList.mHead;
|
|
DbgVariable* typeVariable = bfType->mMemberList.mTail;
|
|
|
|
if ((lenVariable != NULL) && (typeVariable != NULL))
|
|
{
|
|
int len = mDebugger->ReadMemory<int>(collection.mPtr + lenVariable->mMemberOffset);
|
|
int idx = (int)indexArgument.GetInt64();
|
|
if ((idx < 0) || (idx >= len))
|
|
{
|
|
Fail("Index out of range", indexerExpr->mArguments[0]);
|
|
return;
|
|
}
|
|
|
|
auto result = ReadTypedValue(indexerExpr, typeVariable->mType, collection.mPtr + typeVariable->mMemberOffset + (idx * typeVariable->mType->GetStride()), DbgAddrType_Target);
|
|
if (mResult.mIsReadOnly)
|
|
result.mIsReadOnly = true;
|
|
mResult = result;
|
|
return;
|
|
}
|
|
}
|
|
|
|
SetAndRestoreValue<String*> prevReferenceId(mReferenceId, NULL);
|
|
|
|
Array<String> dbgVisWildcardCaptures;
|
|
auto debugVis = mDebugger->FindVisualizerForType(collection.mType, &dbgVisWildcardCaptures);
|
|
if (debugVis != NULL)
|
|
{
|
|
if ((debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array) && (debugVis->mLowerDimSizes.size() == 0))
|
|
{
|
|
auto debugVisualizers = mDebugger->mDebugManager->mDebugVisualizers;
|
|
DbgTypedValue sizeValue = mDebugger->EvaluateInContext(mDbgCompileUnit, collection, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures));
|
|
Array<int> lowerDimSizes;
|
|
if (sizeValue)
|
|
{
|
|
int len = (int)sizeValue.GetInt64();
|
|
int idx = (int)indexArgument.GetInt64();
|
|
if ((idx < 0) || (idx >= len))
|
|
{
|
|
Fail("Index out of range", indexerExpr->mArguments[0]);
|
|
return;
|
|
}
|
|
|
|
addr_target dataPtr = (collection.mType->IsPointer()) ? collection.mPtr : collection.mSrcAddress;
|
|
String ptrUseDataTypeStr = collection.mType->ToStringRaw();
|
|
String ptrUseDataStr = StrFormat("(%s)", ptrUseDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true);
|
|
//String evalStr = "(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + StrFormat("[%d]), this=", idx);
|
|
String evalStr = "*(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + StrFormat(" + %d), this=", idx) + ptrUseDataStr;
|
|
evalStr += ptrUseDataStr;
|
|
auto result = mDebugger->EvaluateInContext(mDbgCompileUnit, collection, evalStr);
|
|
if (mResult.mIsReadOnly)
|
|
result.mIsReadOnly = true;
|
|
mResult = result;
|
|
if (mResult)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
addr_target target = collection.mPtr;
|
|
if (collection.mType->mTypeCode == DbgType_SizedArray)
|
|
target = collection.mSrcAddress;
|
|
|
|
if ((!collection.mType->IsPointer()) && (collection.mType->mTypeCode != DbgType_SizedArray))
|
|
{
|
|
mResult = DbgTypedValue();
|
|
//Fail("Expected pointer type", indexerExpr->mTarget);
|
|
Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket);
|
|
return;
|
|
}
|
|
|
|
if (collection.mType->mTypeCode == DbgType_SizedArray)
|
|
{
|
|
int innerSize = collection.mType->mTypeParam->GetStride();
|
|
int len = 0;
|
|
if (innerSize > 0)
|
|
len = collection.mType->GetStride() / innerSize;
|
|
int idx = (int)indexArgument.GetInt64();
|
|
if ((idx < 0) || (idx >= len))
|
|
{
|
|
Fail("Index out of range", indexerExpr->mArguments[0]);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((collection.mType->IsBfObjectPtr()) || (collection.mType->IsStruct()))
|
|
{
|
|
// This should have been handled by some other cases
|
|
mResult = DbgTypedValue();
|
|
Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket);
|
|
}
|
|
|
|
auto memberType = collection.mType->mTypeParam;
|
|
auto result = ReadTypedValue(indexerExpr, memberType, target + indexArgument.GetInt64() * memberType->GetStride(), DbgAddrType_Target);
|
|
if (mResult.mIsReadOnly)
|
|
result.mIsReadOnly = true;
|
|
mResult = result;
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfThisExpression* thisExpr)
|
|
{
|
|
if (mExplicitThis)
|
|
{
|
|
if (mExplicitThisExpr != NULL)
|
|
mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(thisExpr, mCurChildRef));
|
|
mResult = mExplicitThis;
|
|
|
|
/*if (mReferenceId != NULL)
|
|
{
|
|
auto checkType = mExplicitThis.mType;
|
|
if (checkType->IsPointer())
|
|
checkType = checkType->mTypeParam;
|
|
*mReferenceId = checkType->ToString(true);
|
|
}*/
|
|
|
|
return;
|
|
}
|
|
|
|
bool hadError;
|
|
mResult = LookupIdentifier(thisExpr, false, &hadError);
|
|
if (!mResult)
|
|
{
|
|
if (GetCurrentMethod() != NULL)
|
|
Fail("Static methods don't have 'this'", thisExpr);
|
|
else
|
|
Fail("Execution must be paused to retrieve 'this'", thisExpr);
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfIdentifierNode* identifierNode)
|
|
{
|
|
//BfLogDbgExpr("Visit BfIdentifierNode %s\n", identifierNode->ToString().c_str());
|
|
|
|
mResult = LookupIdentifier(identifierNode, false, NULL);
|
|
if (!mResult)
|
|
{
|
|
//??
|
|
//mDbgModule->EnsureMethodsMapped(identifierNode->ToString().c_str());
|
|
//mResult = LookupIdentifier(identifierNode, false, NULL);
|
|
}
|
|
|
|
if ((mResult) || (HasPropResult()))
|
|
return;
|
|
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
mResult.mType = ResolveTypeRef(identifierNode);
|
|
if (mResult.mType != NULL)
|
|
{
|
|
mResult.mHasNoValue = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Fail("Identifier not found", identifierNode);
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfAttributedIdentifierNode* node)
|
|
{
|
|
VisitChild(node->mIdentifier);
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfMixinExpression* mixinExpr)
|
|
{
|
|
mResult = LookupIdentifier(mixinExpr, false, NULL);
|
|
if (mResult)
|
|
return;
|
|
Fail("Identifier not found", mixinExpr);
|
|
}
|
|
|
|
void DbgExprEvaluator::LookupSplatMember(const DbgTypedValue& target, const StringImpl& fieldName)
|
|
{
|
|
BF_ASSERT(target.mSrcAddress == -1);
|
|
|
|
DbgVariable* dbgVariable = (DbgVariable*)target.mVariable;
|
|
if (dbgVariable == NULL)
|
|
return;
|
|
|
|
auto wantType = target.mType->RemoveModifiers();
|
|
auto checkType = dbgVariable->mType->RemoveModifiers();
|
|
|
|
// Use real name, in case of aliases
|
|
String findName = target.mVariable->mName;
|
|
|
|
bool foundWantType = false;
|
|
|
|
/*if (checkType != wantType)
|
|
{
|
|
checkType = valType;
|
|
foundWantType = false;
|
|
|
|
if (checkType->IsBfPayloadEnum())
|
|
{
|
|
if (wasCast)
|
|
{
|
|
findName += "$u";
|
|
checkType = wantType;
|
|
foundWantType = true;
|
|
}
|
|
}
|
|
}*/
|
|
|
|
bool found = false;
|
|
while (checkType != NULL)
|
|
{
|
|
checkType = checkType->GetPrimaryType();
|
|
if ((!foundWantType) && (checkType->Equals(wantType)))
|
|
foundWantType = true;
|
|
|
|
if (foundWantType)
|
|
{
|
|
for (auto member : checkType->mMemberList)
|
|
{
|
|
if (member->mName == fieldName)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
break;
|
|
checkType = checkType->GetBaseType();
|
|
findName += "$b";
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
findName = "$" + findName + "$m$" + fieldName;
|
|
|
|
CPUStackFrame* stackFrame = GetStackFrame();
|
|
intptr valAddr;
|
|
DbgType* valType;
|
|
DbgAddrType addrType = DbgAddrType_Value;
|
|
|
|
if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
BF_ASSERT(valType != NULL);
|
|
|
|
if (valType != NULL)
|
|
{
|
|
if (mReferenceId != NULL)
|
|
*mReferenceId = findName;
|
|
mResult = ReadTypedValue(NULL, valType, valAddr, addrType);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::LookupSplatMember(BfAstNode* targetNode, BfAstNode* lookupNode, const DbgTypedValue& target, const StringImpl& fieldName, String* outFindName, bool* outIsConst, StringImpl* forceName)
|
|
{
|
|
/*DbgVariable* dbgVariable = (DbgVariable*)target.mVariable;
|
|
if (dbgVariable != NULL)
|
|
return LookupSplatMember(target, fieldName);*/
|
|
|
|
auto curMethod = GetCurrentMethod();
|
|
|
|
while (auto parenNode = BfNodeDynCast<BfParenthesizedExpression>(lookupNode))
|
|
lookupNode = parenNode->mExpression;
|
|
|
|
SplatLookupEntry* splatLookupEntry = NULL;
|
|
if (lookupNode != NULL)
|
|
{
|
|
if (mSplatLookupMap.TryAdd(lookupNode, NULL, &splatLookupEntry))
|
|
{
|
|
//
|
|
}
|
|
else if (forceName == NULL)
|
|
{
|
|
if (outFindName != NULL)
|
|
*outFindName = splatLookupEntry->mFindName;
|
|
if (outIsConst != NULL)
|
|
*outIsConst = splatLookupEntry->mIsConst;
|
|
mResult = splatLookupEntry->mResult;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
String findName;
|
|
|
|
bool wasCast = false;
|
|
mResult = DbgTypedValue();
|
|
BfAstNode* checkNode = targetNode;
|
|
BfAstNode* parentNode = NULL;
|
|
while (true)
|
|
{
|
|
if (checkNode == NULL)
|
|
return;
|
|
if (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(checkNode))
|
|
{
|
|
parentNode = qualifiedNameNode->mLeft;
|
|
findName = qualifiedNameNode->mRight->ToString();
|
|
break;
|
|
}
|
|
else if (auto memberRefExpr = BfNodeDynCast<BfMemberReferenceExpression>(checkNode))
|
|
{
|
|
parentNode = memberRefExpr->mTarget;
|
|
findName = memberRefExpr->mMemberName->ToString();
|
|
break;
|
|
}
|
|
else if (auto identifier = BfNodeDynCast<BfIdentifierNode>(checkNode))
|
|
{
|
|
findName = identifier->ToString();
|
|
break;
|
|
}
|
|
else if (auto thisExpr = BfNodeDynCast<BfThisExpression>(checkNode))
|
|
{
|
|
findName = thisExpr->ToString();
|
|
break;
|
|
}
|
|
else if (auto castExpr = BfNodeDynCast<BfCastExpression>(checkNode))
|
|
{
|
|
wasCast = true;
|
|
checkNode = castExpr->mExpression;
|
|
}
|
|
else if (auto parenExpr = BfNodeDynCast<BfParenthesizedExpression>(checkNode))
|
|
{
|
|
checkNode = parenExpr->mExpression;
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (forceName != NULL)
|
|
findName = *forceName;
|
|
|
|
CPUStackFrame* stackFrame = GetStackFrame();
|
|
intptr valAddr;
|
|
DbgType* valType = NULL;
|
|
DbgAddrType addrType = DbgAddrType_Value;
|
|
|
|
bool foundWantType = true;
|
|
DbgType* wantType = target.mType;
|
|
auto checkType = target.mType;
|
|
checkType = checkType->RemoveModifiers();
|
|
bool valWasConst = false;
|
|
|
|
bool foundParent = false;
|
|
if (target.mSrcAddress == -1)
|
|
{
|
|
if (parentNode != NULL)
|
|
{
|
|
DbgTypedValue origTarget;
|
|
origTarget.mSrcAddress = -1;
|
|
origTarget.mType = target.mVariable->mType;
|
|
origTarget.mVariable = target.mVariable;
|
|
|
|
bool parentIsConst = false;
|
|
String parentFindName;
|
|
LookupSplatMember(parentNode, targetNode, origTarget, findName, &parentFindName, &parentIsConst);
|
|
if (!parentFindName.IsEmpty())
|
|
{
|
|
foundParent = true;
|
|
findName = parentFindName;
|
|
valType = mResult.mType;
|
|
valWasConst = parentIsConst;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundParent)
|
|
{
|
|
if (!mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType))
|
|
return;
|
|
|
|
if (addrType == DbgAddrType_Alias)
|
|
{
|
|
findName = (const char*)valAddr;
|
|
|
|
if (!mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
if (curMethod->mInlineeInfo != NULL)
|
|
{
|
|
// Look outside to inline
|
|
SetAndRestoreValue<int> prevStackIdx(mCallStackIdx, mCallStackIdx + 1);
|
|
LookupSplatMember(targetNode, lookupNode, target, fieldName, outFindName, outIsConst, &findName);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (addrType == DbgAddrType_LocalSplat)
|
|
{
|
|
DbgVariable* dbgVariable = (DbgVariable*)valAddr;
|
|
// Use real name, in case of aliases
|
|
findName = dbgVariable->mName;
|
|
}
|
|
}
|
|
|
|
if ((wasCast) && (valType != NULL))
|
|
{
|
|
checkType = valType->RemoveModifiers();
|
|
foundWantType = false;
|
|
|
|
if (checkType->IsBfPayloadEnum())
|
|
{
|
|
if (wasCast)
|
|
{
|
|
findName += "$u";
|
|
checkType = wantType;
|
|
foundWantType = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
mResult = DbgTypedValue();
|
|
if (valType != NULL)
|
|
valWasConst |= valType->IsConst();
|
|
DbgType* memberType = NULL;
|
|
bool found = false;
|
|
bool wasUnion = false;
|
|
while (checkType != NULL)
|
|
{
|
|
checkType = checkType->GetPrimaryType();
|
|
if ((!foundWantType) && (checkType->Equals(wantType)))
|
|
foundWantType = true;
|
|
|
|
if (foundWantType)
|
|
{
|
|
for (auto member : checkType->mMemberList)
|
|
{
|
|
if (member->mName == fieldName)
|
|
{
|
|
wasUnion = checkType->IsBfUnion();
|
|
memberType = member->mType;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
break;
|
|
checkType = checkType->GetBaseType();
|
|
findName += "$b";
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
bool wantConst = ((valWasConst) && (!memberType->IsConst()) && (!memberType->IsRef()));
|
|
if (!findName.StartsWith("$"))
|
|
findName = "$" + findName;
|
|
if (wasUnion)
|
|
findName += "$u";
|
|
else
|
|
findName += "$m$" + fieldName;
|
|
|
|
if (outFindName != NULL)
|
|
*outFindName = findName;
|
|
if (outIsConst != NULL)
|
|
*outIsConst = wantConst;
|
|
|
|
if (mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType))
|
|
{
|
|
BF_ASSERT(valType != NULL);
|
|
|
|
if (valType != NULL)
|
|
{
|
|
if (mReferenceId != NULL)
|
|
*mReferenceId = findName;
|
|
/*if ((valType->IsConst()) && (!memberType->IsConst()))
|
|
{
|
|
// Set readonly if the original value was const
|
|
memberType = mDbgModule->GetConstType(valType);
|
|
}*/
|
|
mResult = ReadTypedValue(targetNode, memberType, valAddr, addrType);
|
|
if (wantConst)
|
|
{
|
|
// Set readonly if the original value was const
|
|
mResult.mIsReadOnly = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (target.mSrcAddress == -1)
|
|
{
|
|
// if (curMethod->mInlineeInfo != NULL)
|
|
// {
|
|
// // Look outside to inline
|
|
// SetAndRestoreValue<int> prevStackIdx(mCallStackIdx, mCallStackIdx + 1);
|
|
// LookupSplatMember(targetNode, lookupNode, target, fieldName, outFindName, outIsConst, true);
|
|
// return;
|
|
// }
|
|
|
|
if (!memberType->IsStruct())
|
|
Fail("Failed to lookup splat member", (lookupNode != NULL) ? lookupNode : targetNode);
|
|
|
|
if ((target.mVariable == NULL) && (target.mType->GetByteCount() != 0))
|
|
Fail("Splat variable not found", (lookupNode != NULL) ? lookupNode : targetNode);
|
|
|
|
//BF_ASSERT((target.mVariable != NULL) || (target.mType->GetByteCount() == 0));
|
|
mResult = target;
|
|
mResult.mType = memberType;
|
|
}
|
|
}
|
|
|
|
if (splatLookupEntry != NULL)
|
|
{
|
|
splatLookupEntry->mFindName = findName;
|
|
splatLookupEntry->mIsConst = wantConst;
|
|
splatLookupEntry->mResult = mResult;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::LookupQualifiedName(BfQualifiedNameNode* nameNode, bool ignoreInitialError, bool* hadError)
|
|
{
|
|
String fieldName = nameNode->mRight->ToString();
|
|
|
|
mResult = LookupIdentifier(nameNode->mLeft, ignoreInitialError, hadError);
|
|
mResult = GetResult();
|
|
|
|
if (!mResult)
|
|
{
|
|
if (!ignoreInitialError)
|
|
Fail("Identifier not found", nameNode->mLeft);
|
|
return;
|
|
}
|
|
|
|
if (!mResult)
|
|
return;
|
|
|
|
/*if (!mResult.mType->IsTypeInstance())
|
|
{
|
|
Fail("Type has no fields", nameNode->mLeft);
|
|
return;
|
|
}*/
|
|
|
|
if (mResult.mSrcAddress == -1)
|
|
{
|
|
auto target = mResult;
|
|
mResult = DbgTypedValue();
|
|
LookupSplatMember(nameNode->mLeft, nameNode, target, fieldName);
|
|
}
|
|
else
|
|
{
|
|
mResult = LookupField(nameNode, mResult, fieldName);
|
|
if (mPropSrc != NULL)
|
|
{
|
|
if (nameNode->mLeft->ToString() == "base")
|
|
{
|
|
//mPropDefBypassVirtual = true;
|
|
}
|
|
}
|
|
}
|
|
if ((mResult) || (mPropSrc != NULL))
|
|
return;
|
|
|
|
if (hadError != NULL)
|
|
*hadError = true;
|
|
Fail("Unable to find member", nameNode->mRight);
|
|
}
|
|
|
|
DbgType* DbgExprEvaluator::FindSubtype(DbgType* type, const StringImpl& name)
|
|
{
|
|
for (auto subType : type->mSubTypeList)
|
|
{
|
|
if ((subType->mTypeName != NULL) && (strcmp(subType->mTypeName, name.c_str()) == 0))
|
|
return subType;
|
|
}
|
|
for (auto baseType : type->mBaseTypes)
|
|
{
|
|
auto subType = FindSubtype(baseType->mBaseType->GetPrimaryType(), name);
|
|
if (subType != NULL)
|
|
return subType;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void DbgExprEvaluator::LookupQualifiedStaticField(BfQualifiedNameNode* nameNode, bool ignoreIdentifierNotFoundError)
|
|
{
|
|
// Lookup left side as a type
|
|
{
|
|
DbgType* type;
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErro(mIgnoreErrors, true);
|
|
type = ResolveTypeRef(nameNode->mLeft);
|
|
}
|
|
if (type != NULL)
|
|
{
|
|
DbgTypedValue lookupType;
|
|
/*if (type->IsPrimitiveType())
|
|
lookupType.mType = mModule->GetPrimitiveStructType(type));
|
|
else*/
|
|
lookupType.mType = type;
|
|
lookupType.mHasNoValue = true;
|
|
mResult = LookupField(nameNode->mRight, lookupType, nameNode->mRight->ToString());
|
|
if ((mResult) || (mPropSrc != NULL))
|
|
return;
|
|
|
|
DbgType* fullType = NULL;
|
|
//
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
fullType = ResolveTypeRef(nameNode);
|
|
if ((fullType == NULL) && (!type->IsNamespace()))
|
|
{
|
|
String subName = nameNode->mRight->ToString();
|
|
fullType = FindSubtype(type, subName);
|
|
}
|
|
}
|
|
|
|
if (fullType != NULL)
|
|
{
|
|
mResult.mType = fullType;
|
|
mResult.mHasNoValue = true;
|
|
return;
|
|
}
|
|
|
|
Fail("Field not found", nameNode->mRight);
|
|
return;
|
|
}
|
|
}
|
|
|
|
String fieldName = nameNode->mRight->ToString();
|
|
|
|
if (auto qualifiedLeftName = BfNodeDynCast<BfQualifiedNameNode>(nameNode->mLeft))
|
|
LookupQualifiedStaticField(qualifiedLeftName);
|
|
else
|
|
VisitChild(nameNode->mLeft);
|
|
GetResult();
|
|
|
|
if (!mResult)
|
|
{
|
|
Fail("Identifier not found", nameNode->mLeft);
|
|
return;
|
|
}
|
|
|
|
if (!mResult)
|
|
return;
|
|
|
|
auto checkType = mResult.mType->RemoveModifiers();
|
|
if (checkType->IsPointer())
|
|
checkType = checkType->mTypeParam;
|
|
if ((checkType == NULL) || (!checkType->IsCompositeType()))
|
|
{
|
|
Fail("Type has no fields", nameNode->mLeft);
|
|
return;
|
|
}
|
|
|
|
mResult = LookupField(nameNode, mResult, fieldName);
|
|
if ((mResult) || (mPropSrc != NULL))
|
|
return;
|
|
|
|
Fail("Unable to find member", nameNode->mRight);
|
|
}
|
|
|
|
bool DbgExprEvaluator::EnsureRunning(BfAstNode* astNode)
|
|
{
|
|
if (mDebugger->mIsRunning)
|
|
return true;
|
|
Fail("Target is not running", astNode);
|
|
return false;
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfQualifiedNameNode* nameNode)
|
|
{
|
|
AutocompleteCheckMemberReference(nameNode->mLeft, nameNode->mDot, nameNode->mRight);
|
|
|
|
bool hadError = false;
|
|
LookupQualifiedName(nameNode, true, &hadError);
|
|
if ((mResult) || (mPropSrc != NULL))
|
|
return;
|
|
if (hadError)
|
|
return;
|
|
|
|
LookupQualifiedStaticField(nameNode);
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfDefaultExpression* defaultExpr)
|
|
{
|
|
mIsComplexExpression = true;
|
|
|
|
/*auto type = mModule->ResolveTypeRef(defaultExpr->mTypeRef);
|
|
if (!type)
|
|
return;
|
|
mResult = DbgTypedValue(mModule->GetDefaultValue(type), type);*/
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfLiteralExpression* literalExpr)
|
|
{
|
|
mIsComplexExpression = true;
|
|
|
|
mResult.mIsLiteral = true;
|
|
|
|
auto language = GetLanguage();
|
|
switch (literalExpr->mValue.mTypeCode)
|
|
{
|
|
case BfTypeCode_NullPtr:
|
|
{
|
|
mResult.mPtr = 0;
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Null, GetLanguage());
|
|
}
|
|
break;
|
|
case BfTypeCode_CharPtr:
|
|
mResult = GetString(*literalExpr->mValue.mString);
|
|
break;
|
|
case BfTypeCode_Boolean:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
mResult.mBool = literalExpr->mValue.mBool;
|
|
break;
|
|
case BfTypeCode_Char8:
|
|
mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar : DbgType_SChar, language);
|
|
mResult.mInt8 = literalExpr->mValue.mInt8;
|
|
break;
|
|
case BfTypeCode_Char16:
|
|
mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar16 : DbgType_SChar16, language);
|
|
mResult.mInt16 = literalExpr->mValue.mInt16;
|
|
break;
|
|
case BfTypeCode_Char32:
|
|
mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar32 : DbgType_SChar32, language);
|
|
mResult.mInt32 = literalExpr->mValue.mInt32;
|
|
break;
|
|
case BfTypeCode_Int8:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i8, GetLanguage());
|
|
mResult.mUInt8 = literalExpr->mValue.mUInt8;
|
|
break;
|
|
case BfTypeCode_UInt8:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u8, GetLanguage());
|
|
mResult.mUInt8 = literalExpr->mValue.mUInt8;
|
|
break;
|
|
case BfTypeCode_Int16:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i16, GetLanguage());
|
|
mResult.mInt16 = literalExpr->mValue.mInt16;
|
|
break;
|
|
case BfTypeCode_UInt16:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u16, GetLanguage());
|
|
mResult.mUInt16 = literalExpr->mValue.mUInt16;
|
|
break;
|
|
case BfTypeCode_Int32:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
mResult.mInt32 = literalExpr->mValue.mInt32;
|
|
break;
|
|
case BfTypeCode_UInt32:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u32, GetLanguage());
|
|
mResult.mUInt32 = literalExpr->mValue.mUInt32;
|
|
break;
|
|
case BfTypeCode_Int64:
|
|
case BfTypeCode_IntUnknown:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage());
|
|
mResult.mInt64 = literalExpr->mValue.mInt64;
|
|
break;
|
|
case BfTypeCode_UInt64:
|
|
case BfTypeCode_UIntUnknown:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u64, GetLanguage());
|
|
mResult.mUInt64 = literalExpr->mValue.mUInt64;
|
|
break;
|
|
case BfTypeCode_IntPtr:
|
|
if (sizeof(addr_target) == 8)
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage());
|
|
mResult.mInt64 = literalExpr->mValue.mInt64;
|
|
}
|
|
else
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
mResult.mInt32 = literalExpr->mValue.mInt32;
|
|
}
|
|
break;
|
|
case BfTypeCode_UIntPtr:
|
|
if (sizeof(addr_target) == 8)
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u64, GetLanguage());
|
|
mResult.mUInt64 = literalExpr->mValue.mUInt64;
|
|
}
|
|
else
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u32, GetLanguage());
|
|
mResult.mUInt32 = literalExpr->mValue.mUInt32;
|
|
}
|
|
break;
|
|
case BfTypeCode_Float:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Single, GetLanguage());
|
|
mResult.mSingle = literalExpr->mValue.mSingle;
|
|
break;
|
|
case BfTypeCode_Double:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Double, GetLanguage());
|
|
mResult.mDouble = literalExpr->mValue.mDouble;
|
|
break;
|
|
default:
|
|
Fail("Invalid literal", literalExpr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfCastExpression* castExpr)
|
|
{
|
|
mIsComplexExpression = true;
|
|
|
|
if (castExpr->mTypeRef == NULL)
|
|
return; // Error
|
|
|
|
AutocompleteCheckType(castExpr->mTypeRef);
|
|
auto resolvedType = ResolveTypeRef(castExpr->mTypeRef);
|
|
if (resolvedType == NULL)
|
|
return;
|
|
|
|
if (mExplicitThisExpr != NULL)
|
|
mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(castExpr->mTypeRef, (BfAstNode**)&castExpr->mTypeRef));
|
|
|
|
mResult = CreateValueFromExpression(castExpr->mExpression);
|
|
if (!mResult)
|
|
return;
|
|
mResult = Cast(castExpr, mResult, resolvedType, true);
|
|
}
|
|
|
|
// We pass by reference to support "RAW" debug expression simplification
|
|
DbgTypedValue DbgExprEvaluator::CreateValueFromExpression(ASTREF(BfExpression*)& expr, DbgType* castToType, DbgEvalExprFlags flags)
|
|
{
|
|
//
|
|
{
|
|
BP_ZONE("DbgExprEvaluator::CreateValueFromExpression:CheckStack");
|
|
|
|
StackHelper stackHelper;
|
|
if (!stackHelper.CanStackExpand(64 * 1024))
|
|
{
|
|
DbgTypedValue result;
|
|
if (!stackHelper.Execute([&]()
|
|
{
|
|
result = CreateValueFromExpression(expr, castToType, flags);
|
|
}))
|
|
{
|
|
Fail("Expression too complex", expr);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
mExpectingType = castToType;
|
|
VisitChild(expr);
|
|
GetResult();
|
|
auto result = mResult;
|
|
if ((result) && (castToType != NULL) && ((flags & DbgEvalExprFlags_NoCast) == 0))
|
|
result = Cast(expr, result, castToType, false);
|
|
mResult = DbgTypedValue();
|
|
if ((result.mHasNoValue) && ((flags & DbgEvalExprFlags_AllowTypeResult) == 0))
|
|
Fail("Value expected", expr);
|
|
return result;
|
|
}
|
|
|
|
void DbgExprEvaluator::PerformBinaryOperation(ASTREF(BfExpression*)& leftExpression, ASTREF(BfExpression*)& rightExpression, BfBinaryOp binaryOp, BfTokenNode* opToken, bool forceLeftType)
|
|
{
|
|
DbgTypedValue leftValue;
|
|
if (leftExpression != NULL)
|
|
leftValue = CreateValueFromExpression(leftExpression, mExpectingType, DbgEvalExprFlags_NoCast);
|
|
DbgTypedValue rightValue;
|
|
if (rightExpression == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (!leftValue)
|
|
{
|
|
VisitChild(rightExpression);
|
|
return;
|
|
}
|
|
|
|
leftValue.mType = leftValue.mType->RemoveModifiers();
|
|
|
|
/*if (leftValue.mType->IsRef())
|
|
leftValue.mType = leftValue.mType->GetUnderlyingType();*/
|
|
|
|
if ((binaryOp == BfBinaryOp_ConditionalAnd) || (binaryOp == BfBinaryOp_ConditionalOr))
|
|
{
|
|
bool isAnd = binaryOp == BfBinaryOp_ConditionalAnd;
|
|
|
|
if (!leftValue.mType->IsBoolean())
|
|
{
|
|
Fail(StrFormat("Operator requires boolean operand. Left hand side is '%s", leftValue.mType->ToString().c_str()), opToken);
|
|
return;
|
|
}
|
|
|
|
auto boolType = leftValue.mType;
|
|
|
|
if (isAnd)
|
|
{
|
|
if (!leftValue.mBool)
|
|
{
|
|
mResult = leftValue;
|
|
return;
|
|
}
|
|
|
|
rightValue = CreateValueFromExpression(rightExpression);
|
|
if (rightValue)
|
|
rightValue = Cast(rightExpression, rightValue, boolType);
|
|
mResult = rightValue;
|
|
}
|
|
else
|
|
{
|
|
if (leftValue.mBool)
|
|
{
|
|
mResult = leftValue;
|
|
return;
|
|
}
|
|
|
|
rightValue = CreateValueFromExpression(rightExpression);
|
|
if (rightValue)
|
|
rightValue = Cast(rightExpression, rightValue, boolType);
|
|
mResult = rightValue;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((binaryOp == BfBinaryOp_NullCoalesce) && ((leftValue.mType->IsPointer()) || (leftValue.mType->IsBfObject())))
|
|
{
|
|
if (leftValue.mPtr != 0)
|
|
{
|
|
mResult = leftValue;
|
|
return;
|
|
}
|
|
|
|
CreateValueFromExpression(rightExpression, leftValue.mType);
|
|
return;
|
|
}
|
|
|
|
rightValue = CreateValueFromExpression(rightExpression, mExpectingType, DbgEvalExprFlags_NoCast);
|
|
if ((!leftValue) || (!rightValue))
|
|
return;
|
|
|
|
rightValue.mType = rightValue.mType->RemoveModifiers();
|
|
|
|
if (leftValue.mType->IsTypedPrimitive())
|
|
leftValue.mType = leftValue.mType->GetUnderlyingType();
|
|
if (rightValue.mType->IsTypedPrimitive())
|
|
rightValue.mType = rightValue.mType->GetUnderlyingType();
|
|
|
|
// Prefer floats, prefer unsigned
|
|
int leftCompareSize = leftValue.mType->GetByteCount();
|
|
if (leftValue.mType->IsFloat())
|
|
leftCompareSize += 16;
|
|
if (!leftValue.mType->IsPrimitiveType())
|
|
leftCompareSize += 0x100;
|
|
if ((leftValue.mIsLiteral) && (leftValue.mType->IsPointer()))
|
|
leftCompareSize += 0x200;
|
|
|
|
int rightCompareSize = rightValue.mType->GetByteCount();
|
|
if (rightValue.mType->IsFloat())
|
|
rightCompareSize += 16;
|
|
if (!rightValue.mType->IsPrimitiveType())
|
|
rightCompareSize += 0x100;
|
|
if ((rightValue.mIsLiteral) && (rightValue.mType->IsPointer()))
|
|
rightCompareSize += 0x200;
|
|
|
|
/*if ((leftValue.mType->IsTypedPrimitive()) && (rightValue.mType->IsTypedPrimitive()))
|
|
{
|
|
int leftInheritDepth = leftValue.mType->ToTypeInstance()->mInheritDepth;
|
|
int rightInheritDepth = rightValue.mType->ToTypeInstance()->mInheritDepth;
|
|
if (leftInheritDepth < rightInheritDepth)
|
|
{
|
|
// If both are typed primitives then choose the type with the lowest inherit depth
|
|
// so we will choose the base type when applicable
|
|
forceLeftType = true;
|
|
}
|
|
}*/
|
|
|
|
auto resultType = leftValue.mType;
|
|
if (!forceLeftType)
|
|
{
|
|
if ((resultType->IsNull()) ||
|
|
(rightCompareSize > leftCompareSize) ||
|
|
(((rightCompareSize == leftCompareSize) && (!rightValue.mType->IsSigned()))) ||
|
|
(rightValue.mType->IsTypedPrimitive()))
|
|
{
|
|
// Select the type with the "most information"
|
|
if (!rightValue.mType->IsNull())
|
|
resultType = rightValue.mType;
|
|
}
|
|
|
|
if ((!resultType->IsPointer()) && (rightValue.mType->IsPointer()))
|
|
resultType = rightValue.mType;
|
|
}
|
|
|
|
//bool explicitCast = false;
|
|
bool explicitCast = true;
|
|
DbgTypedValue* resultTypedValue;
|
|
DbgTypedValue* otherTypedValue;
|
|
DbgType* otherType;
|
|
BfAstNode* resultTypeSrc;
|
|
BfAstNode* otherTypeSrc;
|
|
|
|
if (resultType == leftValue.mType)
|
|
{
|
|
resultTypedValue = &leftValue;
|
|
resultTypeSrc = leftExpression;
|
|
otherTypedValue = &rightValue;
|
|
otherTypeSrc = rightExpression;
|
|
otherType = otherTypedValue->mType;
|
|
}
|
|
else
|
|
{
|
|
resultTypedValue = &rightValue;
|
|
resultTypeSrc = rightExpression;
|
|
otherTypedValue = &leftValue;
|
|
otherTypeSrc = leftExpression;
|
|
otherType = otherTypedValue->mType;
|
|
}
|
|
|
|
if ((resultTypedValue->mIsLiteral) && (resultType->IsPointer()))
|
|
{
|
|
// If we're comparing against a string literal like 'str == "Hey!"', handle that
|
|
if (BfBinOpEqualityCheck(binaryOp))
|
|
{
|
|
DbgType* useType = otherType;
|
|
useType = useType->RemoveModifiers();
|
|
if (useType->IsPointer())
|
|
useType = useType->mTypeParam;
|
|
|
|
DbgTypedValue useTypedValue = *otherTypedValue;
|
|
if (useTypedValue.mType->IsPointer())
|
|
{
|
|
useTypedValue.mSrcAddress = useTypedValue.mPtr;
|
|
useTypedValue.mType = useTypedValue.mType->mTypeParam;
|
|
}
|
|
|
|
auto language = useType->GetLanguage();
|
|
auto dbgCompileUnit = useType->mCompileUnit;
|
|
auto dbgModule = useType->GetDbgModule();
|
|
Array<String> dbgVisWildcardCaptures;
|
|
auto debugVis = mDebugger->FindVisualizerForType(useType, &dbgVisWildcardCaptures);
|
|
if (debugVis != NULL)
|
|
{
|
|
auto& displayStringList = debugVis->mStringViews;
|
|
String displayString;
|
|
for (auto displayEntry : displayStringList)
|
|
{
|
|
auto& displayStringList = debugVis->mStringViews;
|
|
|
|
DwFormatInfo formatInfo;
|
|
formatInfo.mRawString = true;
|
|
formatInfo.mLanguage = language;
|
|
if (!displayEntry->mCondition.empty())
|
|
{
|
|
if (!mDebugger->EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString))
|
|
continue;
|
|
}
|
|
|
|
String displayStr = mDebugger->mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures);
|
|
mDebugger->ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, displayString, formatInfo, debugVis, false);
|
|
|
|
bool isEq = displayString == resultTypedValue->mCharPtr;
|
|
|
|
auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
mResult.mType = boolType;
|
|
mResult.mBool = isEq == ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Fail("Cannot perform operation with a literal value", resultTypeSrc);
|
|
return;
|
|
}
|
|
|
|
DbgTypedValue convLeftValue;
|
|
DbgTypedValue convRightValue;
|
|
|
|
/*if ((resultType->IsVar()) || (otherType->IsVar()))
|
|
{
|
|
mResult = DbgTypedValue(mModule->GetDefaultValue(resultType), resultType);
|
|
return;
|
|
}*/
|
|
|
|
if ((otherType->IsNull()) && BfBinOpEqualityCheck(binaryOp))
|
|
{
|
|
bool isEquality = (binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality);
|
|
|
|
if (resultType->IsValueType())
|
|
{
|
|
/*if (!mModule->IsInSpecializedGeneric())
|
|
{
|
|
//CS0472: The result of the expression is always 'true' since a value of type 'int' is never equal to 'null' of type '<null>'
|
|
mModule->mCompiler->mPassInstance->Warn(BfWarning_CS0472_ValueTypeNullCompare,
|
|
StrFormat("The result of the expression is always '%s' since a value of type '%s' can never be null",
|
|
isEquality ? "false" : "true", mModule->TypeToString(resultType).c_str()), otherTypeSrc);
|
|
}*/
|
|
|
|
// Valuetypes never equal null
|
|
auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
mResult.mType = boolType;
|
|
mResult.mBool = !isEquality;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//TODO: Use operator methods?
|
|
/*if ((leftValue.mType->IsTypeInstance()) || (rightValue.mType->IsTypeInstance()))
|
|
{
|
|
SmallVector<BfResolvedArg, 2> args;
|
|
BfResolvedArg leftArg;
|
|
leftArg.mExpression = leftExpression;
|
|
leftArg.mTypedValue = leftValue;
|
|
args.push_back(leftArg);
|
|
BfResolvedArg rightArg;
|
|
rightArg.mExpression = rightExpression;
|
|
rightArg.mTypedValue = rightValue;
|
|
args.push_back(rightArg);
|
|
BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL);
|
|
BfBaseClassWalker baseClassWalker(leftValue.mType->ToTypeInstance(), rightValue.mType->ToTypeInstance());
|
|
bool invertResult = false;
|
|
|
|
while (auto checkType = baseClassWalker.Next())
|
|
{
|
|
BfOperatorDef* oppositeOperatorDef = NULL;
|
|
BfBinaryOp oppositeBinaryOp = GetOppositeBinaryOp(binaryOp);
|
|
|
|
for (auto operatorDef : checkType->mTypeDef->mOperators)
|
|
{
|
|
if (operatorDef->mOperatorDeclaration->mBinOp == binaryOp)
|
|
methodMatcher.CheckMethod(checkType, operatorDef);
|
|
else if (operatorDef->mOperatorDeclaration->mBinOp == oppositeBinaryOp)
|
|
oppositeOperatorDef = operatorDef;
|
|
}
|
|
|
|
if ((methodMatcher.mBestMethodDef == NULL) && (oppositeOperatorDef != NULL))
|
|
{
|
|
if (methodMatcher.CheckMethod(checkType, oppositeOperatorDef))
|
|
invertResult = true;
|
|
}
|
|
}
|
|
if (methodMatcher.mBestMethodDef != NULL)
|
|
{
|
|
SetElementType(opToken, BfSourceElementType_Method);
|
|
mResult = CreateCall(&methodMatcher, DbgTypedValue());
|
|
if ((invertResult) && (mResult.mType == mModule->GetPrimitiveType(BfTypeCode_Boolean)))
|
|
mResult.mValue = mModule->mIRBuilder->CreateNot(mResult.mValue);
|
|
return;
|
|
}
|
|
}*/
|
|
|
|
if (resultType->IsPointer() && otherType->IsPointer())
|
|
{
|
|
//TODO: Allow all pointer comparisons, but only allow SUBTRACTION between equal pointer types
|
|
if (binaryOp == BfBinaryOp_Subtract)
|
|
{
|
|
if (!resultType->Equals(otherType))
|
|
{
|
|
Fail(StrFormat("Operands must be the same type, '%s' doesn't match '%s'",
|
|
leftValue.mType->ToString().c_str(), rightValue.mType->ToString().c_str()),
|
|
opToken);
|
|
return;
|
|
}
|
|
|
|
DbgType* intPtrType = mDbgModule->GetPrimitiveType(DbgType_IntPtr_Alias, GetLanguage());
|
|
long ptrDiff = leftValue.mPtr - rightValue.mPtr;
|
|
int elemSize = resultType->mTypeParam->GetStride();
|
|
if (elemSize == 0)
|
|
{
|
|
Fail("Invalid operation on void values", opToken);
|
|
return;
|
|
}
|
|
//BF_ASSERT((ptrDiff % elemSize) == 0);
|
|
mResult.mInt64 = ptrDiff / elemSize;
|
|
mResult.mType = intPtrType;
|
|
return;
|
|
}
|
|
else if ((binaryOp != BfBinaryOp_Equality) && (binaryOp != BfBinaryOp_StrictEquality) &
|
|
(binaryOp != BfBinaryOp_InEquality) && (binaryOp != BfBinaryOp_StrictInEquality) &&
|
|
(binaryOp != BfBinaryOp_LessThan) && (binaryOp != BfBinaryOp_LessThanOrEqual) &&
|
|
(binaryOp != BfBinaryOp_GreaterThan) && (binaryOp != BfBinaryOp_GreaterThanOrEqual))
|
|
{
|
|
Fail("Invalid operation on pointers", opToken);
|
|
return;
|
|
}
|
|
|
|
// Compare them as ints
|
|
resultType = mDbgModule->GetPrimitiveType(DbgType_UIntPtr_Alias, GetLanguage());
|
|
explicitCast = true;
|
|
}
|
|
else if (resultType->IsPointer())
|
|
{
|
|
if (otherType->IsNull())
|
|
{
|
|
if (!BfBinOpEqualityCheck(binaryOp))
|
|
{
|
|
Fail(StrFormat("Invalid operation between '%s' and null", resultType->ToString().c_str()), opToken);
|
|
return;
|
|
}
|
|
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (binaryOp == BfBinaryOp_Equality)
|
|
mResult.mBool = resultTypedValue->mPtr == NULL;
|
|
else
|
|
mResult.mBool = resultTypedValue->mPtr != NULL;
|
|
return;
|
|
}
|
|
|
|
bool isCompare = (binaryOp >= BfBinaryOp_Equality) && (binaryOp <= BfBinaryOp_LessThanOrEqual);
|
|
|
|
// One pointer
|
|
if ((!otherType->IsInteger()) ||
|
|
((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract) && (binaryOp != BfBinaryOp_OverflowAdd) && (binaryOp != BfBinaryOp_OverflowSubtract) && (!isCompare)))
|
|
{
|
|
Fail("Can only add or subtract integer values from pointers", rightExpression);
|
|
return;
|
|
}
|
|
|
|
if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowAdd) || (binaryOp == BfBinaryOp_OverflowSubtract))
|
|
{
|
|
auto underlyingType = otherType->GetUnderlyingType();
|
|
mResult.mType = resultType;
|
|
|
|
DbgType* innerType = resultType->mTypeParam;
|
|
if (binaryOp == BfBinaryOp_Subtract)
|
|
{
|
|
if (resultTypeSrc == rightExpression)
|
|
Fail("Cannot subtract a pointer from an integer", resultTypeSrc);
|
|
|
|
mResult.mPtr = resultTypedValue->mPtr - otherTypedValue->GetInt64() * innerType->GetStride();
|
|
}
|
|
else
|
|
{
|
|
mResult.mPtr = resultTypedValue->mPtr + otherTypedValue->GetInt64() * innerType->GetStride();
|
|
}
|
|
|
|
if (innerType->GetByteCount() == 0)
|
|
{
|
|
Warn("Adding to a pointer to a zero-sized element has no effect", opToken);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((resultType->IsPointer()) || (resultType->IsBfObject()) || (resultType->IsInterface()) /*|| (resultType->IsGenericParam())*/)
|
|
{
|
|
if (!BfBinOpEqualityCheck(binaryOp))
|
|
{
|
|
if (resultType->IsPointer())
|
|
Fail("Invalid operation for pointers", opToken);
|
|
else
|
|
Fail("Invalid operation for objects", opToken);
|
|
return;
|
|
}
|
|
|
|
if (otherType->IsNull())
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality))
|
|
mResult.mBool = resultTypedValue->mPtr == NULL;
|
|
else
|
|
mResult.mBool = resultTypedValue->mPtr != NULL;
|
|
}
|
|
else
|
|
{
|
|
bool explicitCast = true;
|
|
if (otherType->IsInteger())
|
|
explicitCast = true;
|
|
|
|
auto convertedValue = Cast(otherTypeSrc, *otherTypedValue, resultType, explicitCast);
|
|
if (!convertedValue)
|
|
return;
|
|
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality))
|
|
mResult.mBool = resultTypedValue->mPtr == convertedValue.mPtr;
|
|
else
|
|
mResult.mBool = resultTypedValue->mPtr != convertedValue.mPtr;
|
|
}
|
|
|
|
return;
|
|
}
|
|
/*else if (resultType->IsTypedPrimitive())
|
|
{
|
|
bool needsOtherCast = true;
|
|
if (otherType != resultType)
|
|
{
|
|
if (otherType->IsPrimitiveType())
|
|
{
|
|
// Allow zero comparisons to match all typed primitives
|
|
if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_InEquality))
|
|
{
|
|
auto intConstant = dyn_cast<ConstantInt>(otherTypedValue->mValue);
|
|
if (intConstant != NULL)
|
|
{
|
|
if (intConstant->getSExtValue() == 0)
|
|
{
|
|
needsOtherCast = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (needsOtherCast)
|
|
{
|
|
// The only purpose of this cast is to potentially throw a casting error
|
|
Value* otherCastResult = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, explicitCast);
|
|
if (otherCastResult == NULL)
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto underlyingType = resultType->GetUnderlyingType();
|
|
|
|
Value* convResultValue = mModule->CastToValue(resultTypeSrc, *resultTypedValue, underlyingType, true);
|
|
Value* convOtherValue = mModule->CastToValue(otherTypeSrc, *otherTypedValue, underlyingType, true);
|
|
|
|
if ((!underlyingType->IsValuelessType()) && ((convResultValue == NULL) || (convOtherValue == NULL)))
|
|
return;
|
|
|
|
if (resultTypedValue == &leftValue)
|
|
PerformBinaryOperation(underlyingType, convResultValue, convOtherValue, binaryOp, opToken);
|
|
else
|
|
PerformBinaryOperation(underlyingType, convOtherValue, convResultValue, binaryOp, opToken);
|
|
if (mResult.mType == underlyingType)
|
|
mResult.mType = resultType;
|
|
return;
|
|
}*/
|
|
else if (((leftValue.mType->IsStruct()) || (leftValue.mType->IsBfObject())) && (!resultType->IsEnum()))
|
|
{
|
|
if (leftValue.mType == rightValue.mType)
|
|
{
|
|
Fail(StrFormat("Operator '%s' cannot be applied to operands of type '%s'",
|
|
opToken->ToString().c_str(),
|
|
TypeToString(leftValue.mType).c_str()), opToken);
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Operator '%s' cannot be applied to operands of type '%s' and '%s'",
|
|
opToken->ToString().c_str(),
|
|
TypeToString(leftValue.mType).c_str(),
|
|
TypeToString(rightValue.mType).c_str()), opToken);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((resultType->IsInteger()) && (!forceLeftType))
|
|
{
|
|
bool isBitwiseExpr =
|
|
(binaryOp == BfBinaryOp_BitwiseAnd) |
|
|
(binaryOp == BfBinaryOp_BitwiseOr) |
|
|
(binaryOp == BfBinaryOp_ExclusiveOr) |
|
|
(binaryOp == BfBinaryOp_LeftShift) |
|
|
(binaryOp == BfBinaryOp_RightShift);
|
|
|
|
// For binary expressions we don't promote to 'int' unless the types don't match
|
|
if ((!isBitwiseExpr) || (leftValue.mType != rightValue.mType))
|
|
{
|
|
if (binaryOp == BfBinaryOp_Subtract)
|
|
{
|
|
|
|
}
|
|
else if (resultType->GetByteCount() < 4)
|
|
{
|
|
resultType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
}
|
|
else if (leftValue.mType != rightValue.mType)
|
|
{
|
|
if ((binaryOp == BfBinaryOp_LeftShift) || (binaryOp == BfBinaryOp_RightShift))
|
|
{
|
|
// For shifts we leave it in the target type if we're already at 'int' or higher
|
|
}
|
|
else if ((!resultType->IsSigned()) && (otherType->IsSigned()))
|
|
{
|
|
/*if (CanCast(*otherTypedValue, resultType))
|
|
{
|
|
// If we can convert the 'other' value implicitly then it's a convertible literal, leave as uint
|
|
}
|
|
else if (resultType->mSize == 4)
|
|
{
|
|
resultType = mDbgModule->GetPrimitiveType(DbgType_i64);
|
|
}
|
|
else
|
|
{
|
|
Fail(StrFormat("Operator cannot be applied to operands of type '%s' and '%s'",
|
|
TypeToString(leftValue.mType).c_str(),
|
|
TypeToString(rightValue.mType).c_str()), opToken);
|
|
return;
|
|
}*/
|
|
|
|
explicitCast = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (convLeftValue == NULL)
|
|
convLeftValue = Cast(leftExpression, leftValue, resultType, explicitCast);
|
|
if (convRightValue == NULL)
|
|
convRightValue = Cast(rightExpression, rightValue, resultType, explicitCast);
|
|
|
|
PerformBinaryOperation(resultType, convLeftValue, convRightValue, binaryOp, opToken);
|
|
}
|
|
|
|
void DbgExprEvaluator::PerformBinaryOperation(DbgType* resultType, DbgTypedValue convLeftValue, DbgTypedValue convRightValue, BfBinaryOp binaryOp, BfTokenNode* opToken)
|
|
{
|
|
if (resultType->IsValuelessType())
|
|
{
|
|
switch (binaryOp)
|
|
{
|
|
case BfBinaryOp_Equality:
|
|
case BfBinaryOp_StrictEquality:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
mResult.mBool = true;
|
|
break;
|
|
case BfBinaryOp_InEquality:
|
|
case BfBinaryOp_StrictInEquality:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
mResult.mBool = false;
|
|
break;
|
|
default:
|
|
Fail("Invalid operation for void", opToken);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((convLeftValue == NULL) || (convRightValue == NULL))
|
|
return;
|
|
|
|
if ((resultType->IsEnum()) || (resultType->IsTypedPrimitive()))
|
|
resultType = resultType->GetUnderlyingType();
|
|
|
|
if (resultType->IsPrimitiveType())
|
|
{
|
|
auto primType = resultType;
|
|
if (primType->mTypeCode == DbgType_Bool)
|
|
{
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
switch (binaryOp)
|
|
{
|
|
case BfBinaryOp_Equality:
|
|
case BfBinaryOp_StrictEquality:
|
|
mResult.mBool = convLeftValue.mBool == convRightValue.mBool;
|
|
break;
|
|
case BfBinaryOp_InEquality:
|
|
case BfBinaryOp_StrictInEquality:
|
|
mResult.mBool = convLeftValue.mBool != convRightValue.mBool;
|
|
break;
|
|
case BfBinaryOp_ConditionalAnd:
|
|
mResult.mBool = convLeftValue.mBool && convRightValue.mBool;
|
|
break;
|
|
case BfBinaryOp_ConditionalOr:
|
|
mResult.mBool = convLeftValue.mBool | convRightValue.mBool;
|
|
break;
|
|
case BfBinaryOp_ExclusiveOr:
|
|
mResult.mBool = convLeftValue.mBool ^ convRightValue.mBool;
|
|
break;
|
|
default:
|
|
Fail("Invalid operation for booleans", opToken);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ((!resultType->IsIntegral()) && (!resultType->IsFloat()))
|
|
{
|
|
Fail(StrFormat("Cannot perform operation on type '%s'", TypeToString(resultType).c_str()), opToken);
|
|
return;
|
|
}
|
|
|
|
mResult.mType = resultType;
|
|
if (resultType->IsInteger())
|
|
{
|
|
switch (binaryOp)
|
|
{
|
|
case BfBinaryOp_BitwiseAnd:
|
|
mResult.mInt64 = convLeftValue.GetInt64() & convRightValue.GetInt64();
|
|
return;
|
|
case BfBinaryOp_BitwiseOr:
|
|
mResult.mInt64 = convLeftValue.GetInt64() | convRightValue.GetInt64();
|
|
return;
|
|
case BfBinaryOp_ExclusiveOr:
|
|
mResult.mInt64 = convLeftValue.GetInt64() ^ convRightValue.GetInt64();
|
|
return;
|
|
case BfBinaryOp_LeftShift:
|
|
mResult.mInt64 = convLeftValue.GetInt64() << convRightValue.GetInt64();
|
|
return;
|
|
case BfBinaryOp_RightShift:
|
|
mResult.mInt64 = convLeftValue.GetInt64() >> convRightValue.GetInt64();
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (binaryOp)
|
|
{
|
|
case BfBinaryOp_Add:
|
|
case BfBinaryOp_OverflowAdd:
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = convLeftValue.mSingle + convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = convLeftValue.mDouble + convRightValue.mDouble;
|
|
else
|
|
mResult.mInt64 = convLeftValue.GetInt64() + convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_Subtract:
|
|
case BfBinaryOp_OverflowSubtract:
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = convLeftValue.mSingle - convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = convLeftValue.mDouble - convRightValue.mDouble;
|
|
else
|
|
mResult.mInt64 = convLeftValue.GetInt64() - convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_Multiply:
|
|
case BfBinaryOp_OverflowMultiply:
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = convLeftValue.mSingle * convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = convLeftValue.mDouble * convRightValue.mDouble;
|
|
else
|
|
mResult.mInt64 = convLeftValue.GetInt64() * convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_Divide:
|
|
{
|
|
bool isZero = false;
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
{
|
|
mResult.mSingle = convLeftValue.mSingle / convRightValue.mSingle;
|
|
}
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
{
|
|
mResult.mDouble = convLeftValue.mDouble / convRightValue.mDouble;
|
|
}
|
|
else
|
|
{
|
|
if (convRightValue.GetInt64() == 0)
|
|
isZero = true;
|
|
else
|
|
mResult.mInt64 = convLeftValue.GetInt64() / convRightValue.GetInt64();
|
|
}
|
|
if (isZero)
|
|
Fail("Divide by zero", opToken);
|
|
}
|
|
break;
|
|
case BfBinaryOp_Modulus:
|
|
{
|
|
bool isZero = false;
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = fmod(convLeftValue.mSingle, convRightValue.mSingle);
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = fmod(convLeftValue.mDouble, convRightValue.mDouble);
|
|
else
|
|
{
|
|
if (convRightValue.GetInt64() == 0)
|
|
isZero = true;
|
|
else
|
|
mResult.mInt64 = convLeftValue.GetInt64() % convRightValue.GetInt64();
|
|
}
|
|
if (isZero)
|
|
Fail("Divide by zero", opToken);
|
|
}
|
|
break;
|
|
case BfBinaryOp_Equality:
|
|
case BfBinaryOp_StrictEquality:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle == convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble == convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() == convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_InEquality:
|
|
case BfBinaryOp_StrictInEquality:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle != convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble != convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() != convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_LessThan:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle < convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble < convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() < convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_LessThanOrEqual:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle <= convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble <= convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() <= convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_GreaterThan:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle > convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble > convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() > convRightValue.GetInt64();
|
|
break;
|
|
case BfBinaryOp_GreaterThanOrEqual:
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (resultType->mTypeCode == DbgType_Single)
|
|
mResult.mBool = convLeftValue.mSingle >= convRightValue.mSingle;
|
|
else if (resultType->mTypeCode == DbgType_Double)
|
|
mResult.mBool = convLeftValue.mDouble >= convRightValue.mDouble;
|
|
else
|
|
mResult.mBool = convLeftValue.GetInt64() >= convRightValue.GetInt64();
|
|
break;
|
|
default:
|
|
Fail("Invalid operation", opToken);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfBinaryOperatorExpression* binOpExpr)
|
|
{
|
|
//mIsComplexExpression = true;
|
|
//PerformBinaryOperation(binOpExpr->mLeft, binOpExpr->mRight, binOpExpr->mOp, binOpExpr->mOpToken, false);
|
|
|
|
// Check for "(double)-1.0" style cast, misidentified as a binary operation
|
|
if ((binOpExpr->mOp == BfBinaryOp_Add) || (binOpExpr->mOp == BfBinaryOp_Subtract) ||
|
|
(binOpExpr->mOp == BfBinaryOp_BitwiseAnd) || (binOpExpr->mOp == BfBinaryOp_Multiply))
|
|
{
|
|
if (auto parenExpr = BfNodeDynCast<BfParenthesizedExpression>(binOpExpr->mLeft))
|
|
{
|
|
BfAstNode* castTypeExpr = BfNodeDynCast<BfIdentifierNode>(parenExpr->mExpression);
|
|
if (castTypeExpr == NULL)
|
|
castTypeExpr = BfNodeDynCast<BfMemberReferenceExpression>(parenExpr->mExpression);
|
|
if (castTypeExpr != NULL)
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreError(mIgnoreErrors, true);
|
|
auto resolvedType = ResolveTypeRef(castTypeExpr);
|
|
prevIgnoreError.Restore();
|
|
|
|
if (resolvedType != NULL)
|
|
{
|
|
if (binOpExpr->mOp == BfBinaryOp_BitwiseAnd)
|
|
{
|
|
PerformUnaryExpression(binOpExpr->mOpToken, BfUnaryOp_AddressOf, binOpExpr->mRight);
|
|
}
|
|
else if (binOpExpr->mOp == BfBinaryOp_Multiply)
|
|
{
|
|
PerformUnaryExpression(binOpExpr->mOpToken, BfUnaryOp_Dereference, binOpExpr->mRight);
|
|
}
|
|
else
|
|
{
|
|
VisitChild(binOpExpr->mRight);
|
|
if (!mResult)
|
|
return;
|
|
}
|
|
|
|
if ((mResult) && (binOpExpr->mOp == BfBinaryOp_Subtract))
|
|
{
|
|
if (mResult.mType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = -mResult.mSingle;
|
|
else if (mResult.mType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = -mResult.mDouble;
|
|
else
|
|
mResult.mInt64 = -mResult.GetInt64();
|
|
}
|
|
|
|
mResult = Cast(binOpExpr, mResult, resolvedType, true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (binOpExpr->mRight == NULL)
|
|
{
|
|
//mModule->AssertErrorState();
|
|
|
|
// We visit the children for autocompletion only
|
|
if (binOpExpr->mLeft != NULL)
|
|
VisitChild(binOpExpr->mLeft);
|
|
|
|
if (mResult)
|
|
{
|
|
//if (mAutoComplete != NULL)
|
|
//mAutoComplete->CheckEmptyStart(binOpExpr->mOpToken, mResult.mType);
|
|
}
|
|
|
|
if (binOpExpr->mRight != NULL)
|
|
VisitChild(binOpExpr->mRight);
|
|
return;
|
|
}
|
|
|
|
PerformBinaryOperation(binOpExpr->mLeft, binOpExpr->mRight, binOpExpr->mOp, binOpExpr->mOpToken, false);
|
|
}
|
|
|
|
|
|
void DbgExprEvaluator::PerformUnaryExpression(BfAstNode* opToken, BfUnaryOp unaryOp, ASTREF(BfExpression*)& expr)
|
|
{
|
|
if (unaryOp != BfUnaryOp_Dereference)
|
|
mIsComplexExpression = true;
|
|
|
|
VisitChild(expr);
|
|
GetResult();
|
|
if (!mResult)
|
|
return;
|
|
|
|
bool wasLiteral = mResult.mIsLiteral;
|
|
mResult.mIsLiteral = false;
|
|
|
|
switch (unaryOp)
|
|
{
|
|
case BfUnaryOp_Decrement:
|
|
case BfUnaryOp_PostDecrement:
|
|
case BfUnaryOp_Increment:
|
|
case BfUnaryOp_PostIncrement:
|
|
auto ptr = mResult;
|
|
ptr.mType = ptr.mType->RemoveModifiers();
|
|
|
|
if (ptr.mType->IsEnum())
|
|
ptr.mType = ptr.mType->mTypeParam;
|
|
|
|
auto val = mResult;
|
|
int add = ((unaryOp == BfUnaryOp_Decrement) || (unaryOp == BfUnaryOp_PostDecrement)) ? -1 : 1;
|
|
switch (ptr.mType->mTypeCode)
|
|
{
|
|
case DbgType_UChar:
|
|
case DbgType_SChar:
|
|
case DbgType_i8:
|
|
case DbgType_u8:
|
|
case DbgType_Utf8:
|
|
val.mUInt8 += add;
|
|
break;
|
|
case DbgType_UChar16:
|
|
case DbgType_SChar16:
|
|
case DbgType_i16:
|
|
case DbgType_u16:
|
|
case DbgType_Utf16:
|
|
val.mUInt16 += add;
|
|
break;
|
|
case DbgType_UChar32:
|
|
case DbgType_SChar32:
|
|
case DbgType_i32:
|
|
case DbgType_u32:
|
|
case DbgType_Utf32:
|
|
val.mUInt32 += add;
|
|
break;
|
|
case DbgType_i64:
|
|
case DbgType_u64:
|
|
val.mUInt64 += add;
|
|
break;
|
|
case DbgType_Single:
|
|
val.mSingle += add;
|
|
break;
|
|
case DbgType_Double:
|
|
val.mDouble += add;
|
|
break;
|
|
default:
|
|
Fail(StrFormat("Unable to perform operation on type '%s'", ptr.mType->ToString()), expr);
|
|
return;
|
|
}
|
|
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowSideEffects) == 0)
|
|
{
|
|
mBlockedSideEffects = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
StoreValue(mResult, val, opToken);
|
|
}
|
|
|
|
if ((unaryOp == BfUnaryOp_Decrement) || (unaryOp == BfUnaryOp_Increment))
|
|
mResult = val;
|
|
return;
|
|
}
|
|
|
|
bool numericFail = false;
|
|
switch (unaryOp)
|
|
{
|
|
case BfUnaryOp_Dereference:
|
|
{
|
|
auto type = mResult.mType->RemoveModifiers();
|
|
if (!type->IsPointer())
|
|
{
|
|
Fail("Operator can only be used on pointer values", opToken);
|
|
return;
|
|
}
|
|
mResult = ReadTypedValue(opToken, type->mTypeParam, mResult.mPtr, DbgAddrType_Target);
|
|
}
|
|
break;
|
|
case BfUnaryOp_AddressOf:
|
|
{
|
|
if ((mResult.mSrcAddress == 0) || (mResult.mSrcAddress == -1))
|
|
{
|
|
if (mResult.mRegNum != -1)
|
|
{
|
|
Fail(StrFormat("Cannot take address of register '%s'", CPURegisters::GetRegisterName(mResult.mRegNum)), opToken);
|
|
return;
|
|
}
|
|
|
|
Fail("Cannot take address of value", opToken);
|
|
return;
|
|
}
|
|
|
|
mResult.mType = mResult.mType->RemoveModifiers();
|
|
|
|
mResult.mType = mDbgModule->GetPointerType(mResult.mType);
|
|
mResult.mPtr = mResult.mSrcAddress;
|
|
mResult.mSrcAddress = 0;
|
|
}
|
|
break;
|
|
case BfUnaryOp_Not:
|
|
{
|
|
auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
if (mResult.mType->mTypeCode != DbgType_Bool)
|
|
{
|
|
Fail("Operator can only be used on boolean values", opToken);
|
|
return;
|
|
}
|
|
mResult.mIsLiteral = wasLiteral;
|
|
mResult.mBool = !mResult.mBool;
|
|
}
|
|
break;
|
|
case BfUnaryOp_Negate:
|
|
{
|
|
if (mResult.mType->IsInteger())
|
|
{
|
|
auto primType = mResult.mType;
|
|
auto wantType = primType;
|
|
|
|
if (wasLiteral)
|
|
{
|
|
// This is a special case where the user entered -0x80000000 (maxint) but we thought "0x80000000" was a uint in the parser
|
|
// which would get expanded to an int64 for this negate. Properly bring back down to an int32
|
|
if ((primType->mTypeCode == DbgType_u32) && (mResult.GetInt64() == -0x80000000LL))
|
|
{
|
|
//mResult = DbgTypedValue(mModule->GetConstValue((int) i64Val), mModule->GetPrimitiveType(DwTypeCode_Int32));
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!primType->IsSigned())
|
|
{
|
|
if (primType->mSize == 1)
|
|
wantType = mDbgModule->GetPrimitiveType(DbgType_i16, GetLanguage());
|
|
else if (primType->mSize == 2)
|
|
wantType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
else if (primType->mSize == 4)
|
|
wantType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage());
|
|
}
|
|
|
|
mResult.mIsLiteral = wasLiteral;
|
|
mResult.mInt64 = -mResult.GetInt64();
|
|
mResult.mType = wantType;
|
|
return;
|
|
}
|
|
|
|
mResult.mIsLiteral = wasLiteral;
|
|
if (mResult.mType->mTypeCode == DbgType_Single)
|
|
mResult.mSingle = -mResult.mSingle;
|
|
else if (mResult.mType->mTypeCode == DbgType_Double)
|
|
mResult.mDouble = -mResult.mDouble;
|
|
else
|
|
numericFail = true;
|
|
}
|
|
break;
|
|
case BfUnaryOp_InvertBits:
|
|
{
|
|
mResult.mIsLiteral = wasLiteral;
|
|
if (mResult.mType->IsInteger())
|
|
{
|
|
switch (mResult.mType->mSize)
|
|
{
|
|
case 1:
|
|
mResult.mUInt8 = ~mResult.mUInt8;
|
|
break;
|
|
case 2:
|
|
mResult.mUInt16 = ~mResult.mUInt16;
|
|
break;
|
|
case 4:
|
|
mResult.mUInt32 = ~mResult.mUInt32;
|
|
break;
|
|
case 8:
|
|
mResult.mUInt64 = ~mResult.mUInt64;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
Fail("Invalid operator for debug expression", opToken);
|
|
break;
|
|
}
|
|
|
|
if (numericFail)
|
|
{
|
|
Fail("Operator can only be used on numeric types", opToken);
|
|
mResult = DbgTypedValue();
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfUnaryOperatorExpression* unaryOpExpr)
|
|
{
|
|
PerformUnaryExpression(unaryOpExpr->mOpToken, unaryOpExpr->mOp, unaryOpExpr->mExpression);
|
|
}
|
|
|
|
bool DbgExprEvaluator::ResolveArgValues(const BfSizedArray<ASTREF(BfExpression*)>& arguments, SizedArrayImpl<DbgTypedValue>& outArgValues)
|
|
{
|
|
for (int argIdx = 0; argIdx < (int) arguments.size(); argIdx++)
|
|
{
|
|
//BfExprEvaluator exprEvaluator(mModule);
|
|
//exprEvaluator.Evaluate(arguments[argIdx]);
|
|
auto arg = arguments[argIdx];
|
|
DbgTypedValue argValue;
|
|
if (arg != NULL)
|
|
argValue = Resolve(arg);
|
|
if (argValue)
|
|
{
|
|
/*if (argValue.mType->IsRef())
|
|
{
|
|
argValue.mIsAddr = false;
|
|
}
|
|
else if (!argValue.mType->IsStruct())
|
|
argValue = mModule->LoadValue(argValue);*/
|
|
}
|
|
outArgValues.push_back(argValue);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::CreateCall(DbgSubprogram* method, DbgTypedValue thisVal, DbgTypedValue structRetVal, bool bypassVirtual, CPURegisters* registers)
|
|
{
|
|
// Why did we have the Not Runing thing for Exception? It means that when we crash we can't execute functions anymore.
|
|
// That doesn't seem good
|
|
/*if (mDebugger->mRunState == RunState_Exception)
|
|
{
|
|
mPassInstance->Fail("Not running");
|
|
return DbgTypedValue();
|
|
}*/
|
|
|
|
#ifdef BF_WANTS_LOG_DBGEXPR
|
|
auto _GetResultString = [&](DbgTypedValue val)
|
|
{
|
|
String result;
|
|
if (val.mSrcAddress != 0)
|
|
{
|
|
result += StrFormat("0x%p ", val.mSrcAddress);
|
|
|
|
int64 vals[4] = { 0 };
|
|
mDebugger->ReadMemory(val.mSrcAddress, 4 * 8, vals);
|
|
|
|
result += StrFormat("%lld %lld %lld %lld", vals[0], vals[1], vals[2], vals[3]);
|
|
}
|
|
else
|
|
{
|
|
result += StrFormat("%lld", val.mInt64);
|
|
}
|
|
return result;
|
|
};
|
|
#endif
|
|
|
|
int curCallResultIdx = mCallResultIdx++;
|
|
if (((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) == 0) || (mCreatedPendingCall))
|
|
{
|
|
BfLogDbgExpr(" BlockedSideEffects\n");
|
|
|
|
mBlockedSideEffects = true;
|
|
return GetDefaultTypedValue(method->mReturnType);
|
|
}
|
|
mHadSideEffects = true;
|
|
|
|
// Our strategy is to create "pending results" for each call in a debug expression, and then we continually re-evaluate the
|
|
// entire expression using the actual returned results for each of those calls until we can finally evaluate it as a whole
|
|
if (curCallResultIdx < (int)mCallResults->size() - 1)
|
|
{
|
|
auto callResult = (*mCallResults)[curCallResultIdx];
|
|
auto result = callResult.mResult;
|
|
|
|
if (!callResult.mSRetData.IsEmpty())
|
|
{
|
|
result = structRetVal;
|
|
mDebugger->WriteMemory(result.mSrcAddress, &callResult.mSRetData[0], (int)callResult.mSRetData.size());
|
|
}
|
|
|
|
BfLogDbgExpr(" using cached results %s\n", _GetResultString(result).c_str());
|
|
|
|
return result;
|
|
}
|
|
else if (curCallResultIdx == (int)mCallResults->size() - 1)
|
|
{
|
|
auto& callResult = (*mCallResults)[curCallResultIdx];
|
|
|
|
CPURegisters newPhysRegisters;
|
|
mDebugger->PopulateRegisters(&newPhysRegisters);
|
|
|
|
DbgTypedValue returnVal;
|
|
if (method->mReturnType != 0)
|
|
{
|
|
if (callResult.mStructRetVal)
|
|
returnVal = callResult.mStructRetVal;
|
|
else
|
|
returnVal = mDebugger->ReadReturnValue(&newPhysRegisters, method->mReturnType);
|
|
bool hadRef = false;
|
|
auto returnType = method->mReturnType->RemoveModifiers(&hadRef);
|
|
if (hadRef)
|
|
{
|
|
returnVal = ReadTypedValue(NULL, returnType, returnVal.mPtr, DbgAddrType_Target);
|
|
returnVal.mType = returnType;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
returnVal.mType = mDbgModule->GetPrimitiveType(DbgType_Void, GetLanguage());
|
|
}
|
|
|
|
BfLogDbgExpr(" using new results %s\n", _GetResultString(returnVal).c_str());
|
|
|
|
mDebugger->RestoreAllRegisters();
|
|
|
|
if ((method->mReturnType->IsCompositeType()) && (mDebugger->CheckNeedsSRetArgument(method->mReturnType)))
|
|
{
|
|
callResult.mSRetData.Resize(method->mReturnType->GetByteCount());
|
|
mDebugger->ReadMemory(returnVal.mSrcAddress, method->mReturnType->GetByteCount(), &callResult.mSRetData[0]);
|
|
}
|
|
callResult.mResult = returnVal;
|
|
|
|
return returnVal;
|
|
}
|
|
else
|
|
{
|
|
BfLogDbgExpr(" new call\n");
|
|
}
|
|
|
|
// It's a new call
|
|
|
|
addr_target startAddr = method->mBlock.mLowPC;
|
|
if ((method->mVTableLoc != -1) && (thisVal))
|
|
{
|
|
addr_target thisPtr;
|
|
bool isBfObject = false;
|
|
if (thisVal.mType->IsCompositeType())
|
|
{
|
|
isBfObject = thisVal.mType->IsBfObject();
|
|
thisPtr = thisVal.mSrcAddress;
|
|
}
|
|
else
|
|
{
|
|
isBfObject = thisVal.mType->IsBfObjectPtr();
|
|
thisPtr = thisVal.mPtr;
|
|
}
|
|
addr_target vtablePtr = mDebugger->ReadMemory<addr_target>(thisPtr);
|
|
if (isBfObject)
|
|
{
|
|
mDebugTarget->GetCompilerSettings();
|
|
if (mDebugTarget->mBfObjectHasFlags)
|
|
vtablePtr &= ~0xFF;
|
|
}
|
|
if (method->mVTableLoc >= 0x100000)
|
|
{
|
|
// Virtual ext method, double indirection
|
|
addr_target extAddr = mDebugger->ReadMemory<addr_target>(vtablePtr + (uint)((method->mVTableLoc>>20) - 1)*sizeof(addr_target));
|
|
startAddr = mDebugger->ReadMemory<addr_target>(extAddr + (uint)(method->mVTableLoc & 0xFFFFF)*sizeof(addr_target));
|
|
}
|
|
else
|
|
startAddr = mDebugger->ReadMemory<addr_target>(vtablePtr + method->mVTableLoc);
|
|
}
|
|
else
|
|
{
|
|
if ((startAddr == 0) && (method->mLinkName != NULL))
|
|
{
|
|
startAddr = mDebugTarget->FindSymbolAddr(method->mLinkName);
|
|
}
|
|
else if ((startAddr == 0) && (method->mName != NULL))
|
|
{
|
|
startAddr = mDebugTarget->FindSymbolAddr(method->mName);
|
|
}
|
|
}
|
|
|
|
if ((startAddr == (addr_target)0) || (startAddr == (addr_target)-1))
|
|
{
|
|
mPassInstance->Fail("Unable to find address for method, possibly due to compiler optimizations.");
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
mDebugger->SaveAllRegisters();
|
|
|
|
BfLogDbg("Starting RunState_DebugEval on thread %d\n", mDebugger->mActiveThread->mThreadId);
|
|
|
|
addr_target prevSP = registers->GetSP();
|
|
|
|
//registers->mIntRegs.efl = 0x244;
|
|
|
|
//mDebugger->PushValue(registers, 0);
|
|
mDebugger->PushValue(registers, 42);
|
|
|
|
*(registers->GetPCRegisterRef()) = startAddr;
|
|
|
|
mDebugger->mDebugEvalSetRegisters = *registers;
|
|
// Set trap flag, which raises "single-step" exception
|
|
// If we step ONTO the PC we set, then that means we have returned from kernel mode
|
|
// so we need to set the registers again (they would have been clobbered due to SetThreadContext bugs).
|
|
// If we step past the PC then we're all good
|
|
registers->mIntRegs.efl |= 0x100;
|
|
mDebugger->SetRegisters(registers);
|
|
//::ResumeThread(mDebugger->mActiveThread->mHThread);
|
|
|
|
//TODO: For debugging stepping:
|
|
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_StepIntoCalls) != 0)
|
|
{
|
|
mDebugger->mDebugManager->mOutMessages.push_back("rehupLoc");
|
|
return DbgTypedValue();
|
|
}
|
|
/*if (true)
|
|
{
|
|
return DbgTypedValue();
|
|
}*/
|
|
|
|
// mDebugger->mActiveThread->mIsAtBreakpointAddress = 0;
|
|
// mDebugger->mActiveThread->mBreakpointAddressContinuing = 0;
|
|
auto prevRunState = mDebugger->mRunState;
|
|
mDebugger->mRunState = RunState_DebugEval;
|
|
mDebugger->mExplicitStopThread = NULL;
|
|
mDebugger->mRequestedStackFrameIdx = -1;
|
|
|
|
BfLogDbg("DbgExprEvaluator::CreateCall %p in thread %d\n", startAddr, mDebugger->mActiveThread->mThreadId);
|
|
|
|
DbgCallResult callResult;
|
|
callResult.mSubProgram = method;
|
|
callResult.mStructRetVal = structRetVal;
|
|
mCallResults->push_back(callResult);
|
|
|
|
mCreatedPendingCall = true;
|
|
return GetDefaultTypedValue(method->mReturnType);
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::CreateCall(BfAstNode* targetSrc, DbgTypedValue target, DbgSubprogram* method, bool bypassVirtual, const BfSizedArray<ASTREF(BfExpression*)>& arguments, SizedArrayImpl<DbgTypedValue>& argValues)
|
|
{
|
|
HashSet<String> splatParams;
|
|
SizedArray<DbgMethodArgument, 4> argPushQueue;
|
|
|
|
//
|
|
{
|
|
for (auto variable : method->mBlock.mVariables)
|
|
{
|
|
if ((variable->mName == NULL) && (variable->mName[0] != '$'))
|
|
continue;
|
|
|
|
const char* dollarPos = strchr(variable->mName + 1, '$');
|
|
if (dollarPos == NULL)
|
|
continue;
|
|
|
|
String varName = String(variable->mName + 1, dollarPos - variable->mName - 1);
|
|
splatParams.Add(varName);
|
|
}
|
|
}
|
|
|
|
int argIdx = 0;
|
|
int paramIdx = 0;
|
|
|
|
auto _PushArg = [&](const DbgTypedValue& typedValue, DbgVariable* param)
|
|
{
|
|
if (typedValue.mType->GetByteCount() == 0)
|
|
return;
|
|
|
|
if ((param != NULL) && (param->mType != NULL) && (param->mType->IsCompositeType()))
|
|
{
|
|
if ((param->mName != NULL) && (splatParams.Contains(param->mName)))
|
|
{
|
|
std::function<void(const DbgTypedValue& typedVal)> _SplatArgs = [&](const DbgTypedValue& typedVal)
|
|
{
|
|
auto type = typedVal.mType->RemoveModifiers();
|
|
|
|
type = type->GetPrimaryType();
|
|
|
|
if (type->IsValuelessType())
|
|
return;
|
|
|
|
if (!type->IsCompositeType())
|
|
{
|
|
auto elemTypedValue = ReadTypedValue(targetSrc, type, typedVal.mSrcAddress, DbgAddrType_Target);
|
|
DbgMethodArgument methodArg;
|
|
methodArg.mTypedValue = elemTypedValue;
|
|
argPushQueue.push_back(methodArg);
|
|
return;
|
|
}
|
|
|
|
auto baseType = type->GetBaseType();
|
|
if (baseType != NULL)
|
|
{
|
|
DbgTypedValue baseValue = typedVal;
|
|
baseValue.mType = baseType;
|
|
_SplatArgs(baseValue);
|
|
}
|
|
|
|
for (auto member : type->mMemberList)
|
|
{
|
|
if ((member->mIsStatic) || (member->mIsConst))
|
|
continue;
|
|
|
|
DbgTypedValue memberValue;
|
|
memberValue.mSrcAddress = typedVal.mSrcAddress + member->mMemberOffset;
|
|
memberValue.mType = member->mType;
|
|
_SplatArgs(memberValue);
|
|
}
|
|
};
|
|
|
|
_SplatArgs(typedValue);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
DbgMethodArgument methodArg;
|
|
methodArg.mTypedValue = typedValue;
|
|
methodArg.mWantsRef = param->mType->IsRef();
|
|
argPushQueue.push_back(methodArg);
|
|
};
|
|
|
|
bool thisByValue = false;
|
|
int methodParamCount = method->mParams.Size();
|
|
if (methodParamCount > 0)
|
|
{
|
|
method->mParams;
|
|
}
|
|
|
|
if (method->mHasThis)
|
|
{
|
|
auto param = method->mParams[paramIdx];
|
|
if ((param->mType != NULL) && (param->mType->IsValueType()))
|
|
thisByValue = true;
|
|
|
|
if (!target)
|
|
{
|
|
Fail(StrFormat("An object reference is required to invoke the non-static method '%s'", method->ToString().c_str()), targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
_PushArg(target, param);
|
|
methodParamCount--;
|
|
}
|
|
else
|
|
{
|
|
if (target.mPtr != 0)
|
|
{
|
|
Fail(StrFormat("Method '%s' cannot be accessed with an instance reference; qualify it with a type name instead", method->ToString().c_str()), targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
}
|
|
|
|
DbgTypedValue expandedParamsArray;
|
|
DbgType* expandedParamsElementType = NULL;
|
|
int extendedParamIdx = 0;
|
|
|
|
int paramOffset = method->mHasThis ? 1 : 0;
|
|
while (true)
|
|
{
|
|
if (paramIdx >= (int)method->mParams.Size() - paramOffset)
|
|
{
|
|
if (argIdx < (int)arguments.size())
|
|
{
|
|
int showArgIdx = (int)method->mParams.Size();
|
|
if (method->mHasThis)
|
|
showArgIdx--;
|
|
|
|
//WTF- what was this "Calling constructor" error about?!
|
|
/*if (methodInstance->mMethodDef->mMethodDeclaration != NULL)
|
|
mModule->mSystem->Warn(StrFormat("Trying to call constructor '%s'", mModule->MethodToString(methodInstance).c_str()), methodInstance->mMethodDef->GetRefNode());*/
|
|
Fail(StrFormat("Too many arguments. Expected %d fewer.", (int)arguments.size() - (methodParamCount)), arguments[showArgIdx]);
|
|
/*if (methodInstance->mMethodDef->mMethodDeclaration != NULL)
|
|
mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodInstance->mMethodDef->mMethodDeclaration);*/
|
|
return DbgTypedValue();
|
|
}
|
|
break;
|
|
}
|
|
|
|
DbgVariable* param = NULL;
|
|
DbgType* wantType = NULL;
|
|
if (expandedParamsElementType != NULL)
|
|
{
|
|
wantType = expandedParamsElementType;
|
|
}
|
|
else
|
|
{
|
|
param = method->mParams[paramIdx + paramOffset];
|
|
wantType = param->mType;
|
|
|
|
//TODO:
|
|
/*if (method->mParams[paramIdx]->mParamType == BfParamType_Params)
|
|
{
|
|
//TODO: Check to see if it's a direct array pass
|
|
|
|
bool isDirectPass = false;
|
|
if (argIdx < (int)arguments.size())
|
|
{
|
|
if (argValues[argIdx].mValue == NULL)
|
|
return DbgTypedValue();
|
|
if (mModule->CanCast(argValues[argIdx], wantType))
|
|
isDirectPass = true;
|
|
}
|
|
|
|
if (!isDirectPass)
|
|
{
|
|
BfArrayType* arrayType = (BfArrayType*) wantType;
|
|
if (arrayType->IsIncomplete())
|
|
mModule->PopulateType(arrayType, true);
|
|
expandedParamsElementType = arrayType->mTypeGenericArguments[0];
|
|
|
|
int arrayClassSize = arrayType->mInstSize - expandedParamsElementType->mSize;
|
|
|
|
int numElements = (int) arguments.size() - argIdx;
|
|
|
|
Value* sizeValue = mModule->GetConstValue(arrayClassSize);
|
|
Value* elementDataSize = mModule->mIRBuilder->CreateMul(mModule->GetConstValue(expandedParamsElementType->mSize), mModule->GetConstValue(numElements));
|
|
sizeValue = mModule->mIRBuilder->CreateAdd(sizeValue, elementDataSize);
|
|
|
|
auto paramsData = mModule->GetHeadIRBuilder()->CreateAlloca(Type::getInt8Ty(*mModule->mLLVMContext), sizeValue, "paramsData");
|
|
|
|
expandedParamsArray = DbgTypedValue(mModule->mIRBuilder->CreateBitCast(paramsData, arrayType->mLLVMType),
|
|
arrayType, false);
|
|
|
|
MatchConstructor(targetSrc, expandedParamsArray, arrayType, SizedArray<BfExpression*, 1>(), false);
|
|
|
|
//TODO: Assert 'length' var is at slot 1
|
|
auto arrayBits = mModule->mIRBuilder->CreateBitCast(expandedParamsArray.mValue, arrayType->mBaseType->mPtrLLVMType);
|
|
if ((arrayType->mBaseType->mFieldInstances.size() < 0) || (arrayType->mBaseType->mFieldInstances[0].GetFieldDef()->mName != "mLength"))
|
|
{
|
|
mModule->Fail("INTERNAL ERROR: Unable to find array 'length' field", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
auto addr = mModule->mIRBuilder->CreateConstInBoundsGEP2_32(arrayBits, 0, 1, "length");
|
|
mModule->mIRBuilder->CreateStore(mModule->GetConstValue(numElements), addr);
|
|
|
|
llvmArgs.push_back(expandedParamsArray.mValue);
|
|
|
|
continue;
|
|
}
|
|
}*/
|
|
}
|
|
|
|
BfExpression* arg = NULL;
|
|
if (argIdx < (int) arguments.size())
|
|
{
|
|
arg = arguments[argIdx];
|
|
}
|
|
else if (mPassInstance != NULL)
|
|
{
|
|
if (expandedParamsArray)
|
|
break;
|
|
|
|
//TODO: Default values
|
|
/*if ((argIdx >= (int) methodInstance->mDefaultValues.size()) || (methodInstance->mDefaultValues[argIdx] == NULL))
|
|
{
|
|
BfAstNode* refNode = targetSrc;
|
|
if (arguments.size() > 0)
|
|
refNode = arguments.back();
|
|
mModule->Fail(StrFormat("Not enough parameters specified. Got %d, expected %d.", arguments.size(), methodInstance->mParamTypes.size()), refNode);
|
|
return DbgTypedValue();
|
|
}
|
|
llvmArgs.push_back(methodInstance->mDefaultValues[argIdx]);*/
|
|
BfAstNode* refNode = targetSrc;
|
|
if (arguments.size() > 0)
|
|
refNode = arguments.back();
|
|
Fail(StrFormat("Not enough parameters specified. Expected %d more.", methodParamCount - (int)arguments.size()), refNode);
|
|
return DbgTypedValue();
|
|
|
|
argIdx++;
|
|
paramIdx++;
|
|
continue;
|
|
}
|
|
|
|
auto argValue = argValues[argIdx];
|
|
if (argValue.mType == NULL)
|
|
return DbgTypedValue();
|
|
|
|
if (arg != NULL)
|
|
{
|
|
/*if (wantType->IsGenericParam())
|
|
{
|
|
auto genericParamType = (BfGenericParamType*) wantType;
|
|
if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
|
|
wantType = moduleMethodInstance.mMethodInstance->mMethodGenericArguments[genericParamType->mGenericParamIdx];
|
|
}*/
|
|
|
|
if ((wantType->mTypeCode == DbgType_Struct) ||
|
|
(wantType->mTypeCode == DbgType_Class) ||
|
|
(wantType->mTypeCode == DbgType_Union))
|
|
{
|
|
if (GetLanguage() != DbgLanguage_Beef)
|
|
{
|
|
Fail(StrFormat("Calling methods that take structs, classes, or unions by value is not supported"), arg);
|
|
return DbgTypedValue();
|
|
}
|
|
}
|
|
|
|
if (wantType->IsRef())
|
|
wantType = wantType->mTypeParam;
|
|
/*{
|
|
if (argValue.mSrcAddress == 0)
|
|
{
|
|
Fail(StrFormat("Unable to get address of argument %s", method->GetParamName(paramIdx).c_str()), arg);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
wantType = mDbgModule->GetPointerType(wantType->mTypeParam);
|
|
argValue.mPtr = argValue.mSrcAddress;
|
|
argValue.mSrcAddress = 0;
|
|
argValue.mType = wantType;
|
|
}*/
|
|
|
|
argValue = Cast(arg, argValue, wantType);
|
|
if (!argValue)
|
|
return DbgTypedValue();
|
|
|
|
/*if (wantType->IsStruct())
|
|
{
|
|
// We need to make a temp and get the addr of that
|
|
if (!argValue.mIsAddr)
|
|
{
|
|
auto tempVar = mModule->GetHeadIRBuilder()->CreateAlloca(argValue.mType->mLLVMType, NULL, "agg.tmp");
|
|
int align = argValue.mType->mAlign;
|
|
tempVar->setAlignment(align);
|
|
mModule->mIRBuilder->CreateStore(argValue.mValue, tempVar);
|
|
//mModule->mIRBuilder->CreateMemCpy(tempVar, argValue.mValue, mModule->GetConstValue(argValue.mType->mDataSize), align, argValue.mType->IsVolatile());
|
|
argValue = DbgTypedValue(tempVar, argValue.mType, true);
|
|
}
|
|
}
|
|
else if (!wantType->IsRef())
|
|
argValue = mModule->LoadValue(argValue);*/
|
|
}
|
|
|
|
if (!argValue)
|
|
{
|
|
Fail("Invalid expression type", arg);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
//TODO:
|
|
/*if (expandedParamsArray)
|
|
{
|
|
auto firstAddr = mModule->mIRBuilder->CreateConstInBoundsGEP2_32(expandedParamsArray.mValue, 0, 1);
|
|
auto indexedAddr = mModule->mIRBuilder->CreateConstInBoundsGEP1_32(firstAddr, extendedParamIdx);
|
|
mModule->mIRBuilder->CreateStore(argValue.mValue, indexedAddr);
|
|
extendedParamIdx++;
|
|
}
|
|
else*/
|
|
{
|
|
//llvmArgs.push_back(argValue.mValue);
|
|
_PushArg(argValue, param);
|
|
paramIdx++;
|
|
}
|
|
argIdx++;
|
|
}
|
|
|
|
return CreateCall(method, argPushQueue, bypassVirtual);
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::CreateCall(DbgSubprogram* method, SizedArrayImpl<DbgMethodArgument>& argPushQueue, bool bypassVirtual)
|
|
{
|
|
BfLogDbgExpr("CreateCall #%d %s", mCallResultIdx, method->mName);
|
|
|
|
if (mDebugger->IsMiniDumpDebugger())
|
|
{
|
|
Fail("Cannot call functions in a minidump", NULL);
|
|
return GetDefaultTypedValue(method->mReturnType);
|
|
}
|
|
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) == 0)
|
|
{
|
|
mBlockedSideEffects = true;
|
|
return GetDefaultTypedValue(method->mReturnType);
|
|
}
|
|
mHadSideEffects = true;
|
|
|
|
// We need current physical registers to make sure we push params in correct stack frame
|
|
CPURegisters registers;
|
|
bool canSetRegisters = mDebugger->PopulateRegisters(®isters);
|
|
|
|
auto* regSP = registers.GetSPRegisterRef();
|
|
if (mCallStackPreservePos != 0)
|
|
{
|
|
if (mCallStackPreservePos <= *regSP)
|
|
*regSP = mCallStackPreservePos;
|
|
}
|
|
|
|
int paramIdx = argPushQueue.size() - 1;
|
|
if (method->mHasThis)
|
|
paramIdx--;
|
|
|
|
bool thisByValue = false;
|
|
if ((method->mHasThis) && (!method->mParams.IsEmpty()))
|
|
{
|
|
auto param = method->mParams[0];
|
|
if ((param->mType != NULL) && (param->mType->IsValueType()))
|
|
thisByValue = true;
|
|
}
|
|
|
|
DbgTypedValue structRetVal;
|
|
if (mDebugger->CheckNeedsSRetArgument(method->mReturnType))
|
|
{
|
|
int retSize = BF_ALIGN(method->mReturnType->GetByteCount(), 16);
|
|
*regSP -= retSize;
|
|
structRetVal.mType = method->mReturnType;
|
|
structRetVal.mSrcAddress = *regSP;
|
|
|
|
// For chained calls we need to leave the sret form the previous calls intact
|
|
mCallStackPreservePos = *regSP;
|
|
}
|
|
|
|
for (int i = (int)argPushQueue.size() - 1; i >= 0; i--)
|
|
{
|
|
auto& arg = argPushQueue[i];
|
|
|
|
// If we have something like an int constant when we need to pass by int&, then we need to allocate
|
|
// on the stack and pass that address
|
|
if (arg.mWantsRef)
|
|
{
|
|
auto& typedValue = arg.mTypedValue;
|
|
auto rootType = typedValue.mType->RemoveModifiers();
|
|
if (arg.mTypedValue.mSrcAddress != 0)
|
|
{
|
|
typedValue.mPtr = typedValue.mSrcAddress;
|
|
}
|
|
else
|
|
{
|
|
int rootSize = rootType->GetByteCount();
|
|
*regSP -= BF_ALIGN(rootSize, 16);
|
|
mDebugger->WriteMemory(*regSP, &arg.mTypedValue.mInt64, rootSize); // Write from debugger memory to target
|
|
typedValue.mPtr = *regSP;
|
|
}
|
|
|
|
typedValue.mType = mDbgModule->GetPointerType(rootType);
|
|
typedValue.mSrcAddress = 0;
|
|
}
|
|
}
|
|
|
|
if (structRetVal.mSrcAddress != 0)
|
|
{
|
|
BfLogDbgExpr(" SRet:0x%p", structRetVal.mSrcAddress);
|
|
mDebugger->AddParamValue(0, method->mHasThis && !thisByValue, ®isters, structRetVal);
|
|
paramIdx++;
|
|
}
|
|
|
|
DbgTypedValue thisVal;
|
|
for (int padCount = 0; true; padCount++)
|
|
{
|
|
auto regSave = registers;
|
|
int paramIdxSave = paramIdx;
|
|
|
|
*regSP -= padCount * sizeof(addr_target);
|
|
|
|
for (int i = (int)argPushQueue.size() - 1; i >= 0; i--)
|
|
{
|
|
auto& arg = argPushQueue[i];
|
|
if ((i == 0) && (method->mHasThis) && (!method->ThisIsSplat()))
|
|
{
|
|
thisVal = arg.mTypedValue;
|
|
addr_target thisAddr;
|
|
if (thisVal.mType->IsCompositeType())
|
|
thisAddr = thisVal.mSrcAddress;
|
|
else
|
|
thisAddr = thisVal.mPtr;
|
|
mDebugger->SetThisRegister(®isters, thisAddr);
|
|
|
|
if (padCount == 0)
|
|
BfLogDbgExpr(" This:0x%p", thisAddr);
|
|
}
|
|
else
|
|
{
|
|
mDebugger->AddParamValue(paramIdx, method->mHasThis, ®isters, arg.mTypedValue);
|
|
paramIdx--;
|
|
}
|
|
}
|
|
|
|
#ifdef BF_DBG_64
|
|
// Stack must be 16-byte aligned
|
|
if ((*regSP & 0xF) == 0)
|
|
break;
|
|
#else
|
|
break;
|
|
#endif
|
|
|
|
registers = regSave;
|
|
paramIdx = paramIdxSave;
|
|
|
|
BF_ASSERT(padCount < 3);
|
|
}
|
|
|
|
return CreateCall(method, thisVal, structRetVal, bypassVirtual, ®isters);
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::MatchMethod(BfAstNode* targetSrc, DbgTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& methodName,
|
|
const BfSizedArray<ASTREF(BfExpression*)>& arguments, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments)
|
|
{
|
|
SetAndRestoreValue<String*> prevReferenceId(mReferenceId, NULL);
|
|
|
|
bool wantCtor = methodName.IsEmpty();
|
|
|
|
mDbgModule->ParseGlobalsData();
|
|
//mDbgModule->ParseSymbolData();
|
|
|
|
SizedArray<DbgTypedValue, 4> argValues;
|
|
if (!ResolveArgValues(arguments, argValues))
|
|
return DbgTypedValue();
|
|
|
|
if ((methodName == "__cast") || (methodName == "__bitcast"))
|
|
{
|
|
if (argValues.size() > 0)
|
|
{
|
|
String typeName = "";
|
|
for (int argIdx = 0; argIdx < (int)argValues.size() - 1; argIdx++)
|
|
{
|
|
auto arg = argValues[argIdx];
|
|
if (!arg)
|
|
return DbgTypedValue();
|
|
if ((arg.mType->IsPointer()) && ((arg.mType->mTypeParam->mTypeCode == DbgType_UChar) || (arg.mType->mTypeParam->mTypeCode == DbgType_SChar)))
|
|
{
|
|
typeName += arg.mCharPtr;
|
|
}
|
|
else if ((arg.mType->mTypeCode == DbgType_i32) || (arg.mType->mTypeCode == DbgType_i64))
|
|
{
|
|
typeName += BfTypeUtils::HashEncode64(arg.mInt64);
|
|
}
|
|
else
|
|
{
|
|
BeefTypeToString(arg, typeName);
|
|
}
|
|
}
|
|
|
|
auto castedType = mDbgModule->FindType(typeName, NULL, GetLanguage(), true);
|
|
if (castedType == NULL)
|
|
{
|
|
if (typeName.EndsWith('*'))
|
|
{
|
|
auto noPtrTypeEntry = mDbgModule->FindType(typeName.Substring(0, typeName.length() - 1), NULL, DbgLanguage_Beef, true);
|
|
if (noPtrTypeEntry != NULL)
|
|
castedType = mDbgModule->GetPointerType(noPtrTypeEntry);
|
|
}
|
|
}
|
|
|
|
int targetArgIdx = argValues.size() - 1;
|
|
if (castedType == NULL)
|
|
{
|
|
Fail(StrFormat("Unable to find type: '%s'", typeName.c_str()), targetSrc);
|
|
return argValues[targetArgIdx];
|
|
}
|
|
|
|
if (methodName == "__bitcast")
|
|
{
|
|
DbgTypedValue result = argValues[targetArgIdx];
|
|
result.mType = castedType;
|
|
return result;
|
|
}
|
|
|
|
return Cast(arguments[targetArgIdx], argValues[targetArgIdx], castedType, true);
|
|
}
|
|
}
|
|
else if (methodName == "__hasField")
|
|
{
|
|
if (argValues.size() == 2)
|
|
{
|
|
auto checkType = argValues[0].mType;
|
|
if ((checkType != NULL) && (checkType->IsPointer()))
|
|
checkType = checkType->mTypeParam;
|
|
if (checkType != NULL)
|
|
{
|
|
|
|
//TODO: Protect
|
|
String findFieldName = argValues[1].mCharPtr;
|
|
|
|
DbgTypedValue result;
|
|
|
|
result.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
|
|
result.mBool = HasField(checkType, findFieldName);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
else if (methodName == "__desc")
|
|
{
|
|
if (argValues.size() == 1)
|
|
{
|
|
auto checkType = argValues[0].mType;
|
|
if ((checkType != NULL) && (checkType->IsPointer()))
|
|
checkType = checkType->mTypeParam;
|
|
|
|
if (checkType != NULL)
|
|
{
|
|
checkType = checkType->GetPrimaryType();
|
|
|
|
String dsc = "DESCRIPTION OF ";
|
|
dsc += checkType->ToString();
|
|
dsc += "\n";
|
|
|
|
dsc += "From Compile Unit: ";
|
|
dsc += checkType->mCompileUnit->mName;
|
|
dsc += "\n";
|
|
|
|
dsc += StrFormat("Size: %d\n", checkType->GetByteCount());
|
|
dsc += StrFormat("Align: %d\n", checkType->GetAlign());
|
|
|
|
for (auto member : checkType->mMemberList)
|
|
{
|
|
dsc += " ";
|
|
if (member->mIsConst)
|
|
dsc += "const ";
|
|
if (member->mIsExtern)
|
|
dsc += "extern ";
|
|
if (member->mIsStatic)
|
|
dsc += "static ";
|
|
dsc += member->mType->ToString();
|
|
dsc += " ";
|
|
dsc += member->mName;
|
|
if (!member->mIsStatic)
|
|
{
|
|
dsc += StrFormat(" Offset:%d", member->mMemberOffset);
|
|
}
|
|
dsc += StrFormat(" Size:%d", member->mType->GetByteCount());
|
|
if (member->mLinkName != NULL)
|
|
dsc += member->mLinkName;
|
|
dsc += "\n";
|
|
}
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0)
|
|
mDebugger->OutputMessage(dsc);
|
|
return DbgTypedValue();
|
|
}
|
|
}
|
|
}
|
|
else if (methodName == "__demangleMethod")
|
|
{
|
|
if (argValues.size() == 2)
|
|
{
|
|
auto checkType = argValues[0].mType;
|
|
if (checkType->IsPointer())
|
|
checkType = checkType->mTypeParam;
|
|
|
|
//TODO: Protect
|
|
String findMethodName = argValues[1].mCharPtr;
|
|
|
|
checkType->PopulateType();
|
|
for (auto checkMethod : checkType->mMethodList)
|
|
{
|
|
if (checkMethod->mName == findMethodName)
|
|
{
|
|
static String demangledName;
|
|
demangledName = BfDemangler::Demangle(checkMethod->mLinkName, checkType->GetLanguage());
|
|
|
|
DbgTypedValue result;
|
|
result.mType = argValues[1].mType;
|
|
result.mCharPtr = demangledName.c_str();
|
|
result.mIsLiteral = true;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (methodName == "__demangle")
|
|
{
|
|
if (argValues.size() == 1)
|
|
{
|
|
auto rawTextType = mDbgModule->GetPrimitiveType(DbgType_RawText, GetLanguage());
|
|
|
|
String mangledName = argValues[0].mCharPtr;
|
|
|
|
static String demangledName;
|
|
demangledName = BfDemangler::Demangle(mangledName, DbgLanguage_Unknown);
|
|
|
|
if (demangledName.StartsWith("bf."))
|
|
demangledName.Remove(0, 3);
|
|
|
|
DbgTypedValue result;
|
|
result.mType = rawTextType;
|
|
result.mCharPtr = demangledName.c_str();
|
|
result.mIsLiteral = true;
|
|
return result;
|
|
}
|
|
}
|
|
else if (methodName == "__demangleFakeMember")
|
|
{
|
|
if (argValues.size() == 1)
|
|
{
|
|
auto checkType = argValues[0].mType;
|
|
if (checkType->IsPointer())
|
|
checkType = checkType->mTypeParam;
|
|
|
|
auto rawTextType = mDbgModule->GetPrimitiveType(DbgType_RawText, GetLanguage());
|
|
|
|
checkType->PopulateType();
|
|
auto firstMember = checkType->mMemberList.mHead;
|
|
|
|
static String demangledName;
|
|
demangledName = BfDemangler::Demangle(firstMember->mName, checkType->GetLanguage());
|
|
|
|
if (demangledName.StartsWith("bf."))
|
|
demangledName.Remove(0, 3);
|
|
|
|
DbgTypedValue result;
|
|
result.mType = rawTextType;
|
|
result.mCharPtr = demangledName.c_str();
|
|
result.mIsLiteral = true;
|
|
return result;
|
|
}
|
|
}
|
|
else if (methodName == "__funcName")
|
|
{
|
|
if (argValues.size() == 1)
|
|
{
|
|
auto funcPtr = argValues[0].mPtr;
|
|
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
static String demangledName;
|
|
auto subProgram = mDebugTarget->FindSubProgram(funcPtr);
|
|
if (subProgram != NULL)
|
|
{
|
|
demangledName = subProgram->ToString();
|
|
}
|
|
else if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf))
|
|
{
|
|
demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Beef);
|
|
}
|
|
else
|
|
{
|
|
demangledName = StrFormat("0x%@", funcPtr);
|
|
}
|
|
|
|
DbgTypedValue result;
|
|
result.mType = mDbgModule->GetPrimitiveType(DbgType_Subroutine, GetLanguage());
|
|
result.mCharPtr = demangledName.c_str();
|
|
return result;
|
|
}
|
|
}
|
|
else if (methodName == "__funcTarget")
|
|
{
|
|
if (argValues.size() == 2)
|
|
{
|
|
auto funcPtr = argValues[0].mPtr;
|
|
auto funcTarget = argValues[1].mPtr;
|
|
|
|
String symbolName;
|
|
addr_target offset;
|
|
DbgModule* dwarf;
|
|
if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf))
|
|
{
|
|
String firstParamType = BfDemangler::Demangle(symbolName, DbgLanguage_Beef, BfDemangler::Flag_CaptureTargetType);
|
|
auto targetType = mDbgModule->FindType(firstParamType, NULL, DbgLanguage_BeefUnfixed);
|
|
if (targetType)
|
|
{
|
|
if (!targetType->IsPointer())
|
|
targetType = mDbgModule->GetPointerType(targetType);
|
|
|
|
DbgTypedValue result;
|
|
result.mType = targetType;
|
|
result.mPtr = funcTarget;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return argValues[1];
|
|
}
|
|
}
|
|
else if (methodName == "__stringView")
|
|
{
|
|
if (argValues.size() >= 2)
|
|
{
|
|
if ((argValues[1].mType != NULL) && (argValues[1].mType->IsInteger()))
|
|
mCountResultOverride = (intptr)argValues[1].GetInt64();
|
|
return argValues[0];
|
|
}
|
|
}
|
|
else if (methodName == "__getHighBits")
|
|
{
|
|
if ((argValues.size() == 2) && (argValues[0]) && (argValues[1]))
|
|
{
|
|
uint64 val = argValues[0].mUInt64;
|
|
int bitCount = (int)argValues[1].GetInt64();
|
|
|
|
DbgTypedValue result;
|
|
result.mType = argValues[0].mType;
|
|
result.mInt64 = val >> (argValues[0].mType->GetByteCount() * 8 - bitCount);
|
|
return result;
|
|
}
|
|
}
|
|
else if (methodName == "__clearHighBits")
|
|
{
|
|
if ((argValues.size() == 2) && (argValues[0]) && (argValues[1]))
|
|
{
|
|
uint64 val = argValues[0].mUInt64;
|
|
int bitCount = (int)argValues[1].GetInt64();
|
|
|
|
if (bitCount <= 0)
|
|
return argValues[0];
|
|
|
|
int64 andBits = (0x8000000000000000LL) >> ((argValues[0].mType->GetByteCount() - 8) * 8 + bitCount - 1);
|
|
DbgTypedValue result;
|
|
result.mType = argValues[0].mType;
|
|
result.mInt64 = val & ~andBits;
|
|
return result;
|
|
}
|
|
}
|
|
else if (methodName == "__parseCompileUnits")
|
|
{
|
|
for (auto dbgModule : mDebugTarget->mDbgModules)
|
|
{
|
|
dbgModule->ParseCompileUnits();
|
|
}
|
|
}
|
|
|
|
DbgType* curTypeDef;
|
|
DbgType* targetTypeInst = NULL;
|
|
bool checkNonStatic = true;
|
|
if (target)
|
|
{
|
|
targetTypeInst = target.mType;
|
|
curTypeDef = targetTypeInst;
|
|
}
|
|
else if (target.mType) // Static targeted
|
|
{
|
|
if (target.mType->IsPrimitiveType())
|
|
{
|
|
//TODO ;
|
|
//targetTypeInst = mModule->GetPrimitiveStructType(target.mType);
|
|
}
|
|
else
|
|
targetTypeInst = target.mType;
|
|
if (targetTypeInst == NULL)
|
|
{
|
|
Fail("No static methods available", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
curTypeDef = targetTypeInst;
|
|
checkNonStatic = false;
|
|
}
|
|
else // Current scope
|
|
{
|
|
curTypeDef = GetCurrentType();
|
|
targetTypeInst = GetCurrentType();
|
|
auto currentMethod = GetCurrentMethod();
|
|
|
|
checkNonStatic = (currentMethod != NULL) && (currentMethod->mHasThis);
|
|
}
|
|
|
|
DbgSubprogram* methodDef = NULL;
|
|
BfTypeVector checkMethodGenericArguments;
|
|
|
|
bool isFailurePass = false;
|
|
DbgType* curTypeInst = targetTypeInst;
|
|
DwMethodMatcher methodMatcher(targetSrc, this, methodName, argValues, methodGenericArguments);
|
|
if (targetTypeInst != NULL)
|
|
methodMatcher.mTargetIsConst = targetTypeInst->IsConst();
|
|
if (targetTypeInst != NULL)
|
|
{
|
|
methodMatcher.CheckType(targetTypeInst, false);
|
|
if (methodMatcher.mBestMethodDef == NULL)
|
|
{
|
|
isFailurePass = true;
|
|
methodMatcher.CheckType(targetTypeInst, true);
|
|
}
|
|
}
|
|
|
|
if (methodMatcher.mBestMethodDef != NULL)
|
|
{
|
|
//methodGenericArguments = methodMatcher.mBestMethodGenericArguments;
|
|
curTypeInst = methodMatcher.mBestMethodTypeInstance;
|
|
methodDef = methodMatcher.mBestMethodDef;
|
|
}
|
|
|
|
if ((methodDef) && (methodDef->mHasThis) && (!target) && (allowImplicitThis))
|
|
{
|
|
target = GetThis();
|
|
/*if (!target)
|
|
{
|
|
mModule->Fail("Target required for non-static method", targetSrc);
|
|
return DbgTypedValue();
|
|
}*/
|
|
}
|
|
|
|
// Fail, check for delegate invocation
|
|
if (methodDef == NULL)
|
|
{
|
|
DbgTypedValue fieldVal;
|
|
if (allowImplicitThis)
|
|
{
|
|
//auto thisValue = mModule->GetThis();
|
|
//LookupField(targetSrc, thisValue, methodName);
|
|
fieldVal = LookupIdentifier(BfNodeDynCast<BfIdentifierNode>(targetSrc));
|
|
}
|
|
else
|
|
{
|
|
fieldVal = LookupField(targetSrc, target, methodName);
|
|
}
|
|
|
|
//TODO:
|
|
/*if (mPropDef != NULL)
|
|
fieldVal = GetResult();*/
|
|
|
|
/*if (fieldVal)
|
|
{
|
|
if (fieldVal.mType->IsTypeInstance())
|
|
{
|
|
auto fieldTypeInst = fieldVal.mType->ToTypeInstance();
|
|
if (fieldTypeInst->mTypeDef->mIsDelegate)
|
|
return MatchMethod(targetSrc, fieldVal, false, false, "Invoke", arguments, methodGenericArguments);
|
|
}
|
|
fieldVal.mType = mModule->ResolveGenericParam(fieldVal.mType);
|
|
if (fieldVal.mType->IsVar())
|
|
return DbgTypedValue(mModule->GetDefaultValue(fieldVal.mType), fieldVal.mType);
|
|
mModule->Fail(StrFormat("Cannot perform invocation on type '%s'", mModule->TypeToString(fieldVal.mType).c_str()), targetSrc);
|
|
return DbgTypedValue();
|
|
}*/
|
|
}
|
|
|
|
/*if ((!methodDef) && (!target))
|
|
{
|
|
String checkTypeName = methodName;
|
|
int wantNumGenericArgs = 0;
|
|
if ((methodGenericArguments != NULL) && (methodGenericArguments->size() > 0))
|
|
wantNumGenericArgs = (int)methodGenericArguments->size();
|
|
auto typeDef = mModule->mSystem->FindTypeDef(checkTypeName, wantNumGenericArgs, mModule->mCurTypeInstance->mTypeDef->mNamespaceSearch);
|
|
if (typeDef != NULL)
|
|
{
|
|
BfTypeVector genericArgs;
|
|
if (methodGenericArguments != NULL)
|
|
{
|
|
for (auto genericArg : *methodGenericArguments)
|
|
{
|
|
auto typeDef = mModule->ResolveTypeRef(genericArg);
|
|
if (typeDef == NULL)
|
|
return DbgTypedValue();
|
|
genericArgs.push_back(typeDef);
|
|
}
|
|
}
|
|
auto resolvedType = mModule->ResolveTypeDef(typeDef, genericArgs)->ToTypeInstance();
|
|
if (resolvedType != NULL)
|
|
{
|
|
if (!resolvedType->IsStruct())
|
|
{
|
|
mModule->Fail("Objects must be allocated through 'new' or 'stack'", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
DbgTypedValue structInst = DbgTypedValue(mModule->mIRBuilder->CreateAlloca(resolvedType->mLLVMType),
|
|
resolvedType, false);
|
|
MatchConstructor(targetSrc, structInst, resolvedType, arguments, false);
|
|
return DbgTypedValue(mModule->mIRBuilder->CreateLoad(structInst.mValue), resolvedType, false);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
if ((methodDef == NULL) && (!target))
|
|
{
|
|
for (int compileUnitIdx = 0; compileUnitIdx < (int)mDbgModule->mCompileUnits.size(); compileUnitIdx++)
|
|
{
|
|
auto compileUnit = mDbgModule->mCompileUnits[compileUnitIdx];
|
|
methodMatcher.CheckType(compileUnit->mGlobalType, isFailurePass);
|
|
if (methodMatcher.mBestMethodDef != NULL)
|
|
{
|
|
isFailurePass = false;
|
|
curTypeInst = methodMatcher.mBestMethodTypeInstance;
|
|
methodDef = methodMatcher.mBestMethodDef;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((methodDef == NULL) && ((methodGenericArguments == NULL) || (methodGenericArguments->IsEmpty())))
|
|
{
|
|
DbgTypedValue enumResult;
|
|
DbgType* enumType = target.mType;
|
|
if ((enumType != NULL) && (enumType->IsBfPayloadEnum()))
|
|
{
|
|
enumResult = CheckEnumCreation(targetSrc, enumType, methodName, arguments);
|
|
if (enumResult)
|
|
return enumResult;
|
|
}
|
|
}
|
|
|
|
if (methodDef == NULL)
|
|
{
|
|
Fail("Method does not exist", targetSrc);
|
|
return DbgTypedValue();
|
|
}
|
|
|
|
// Don't execute the method if we've had a failure (like an ambiguous lookup)
|
|
if ((mPassInstance != NULL) && (mPassInstance->HasFailed()))
|
|
return DbgTypedValue();
|
|
|
|
DbgTypedValue callTarget;
|
|
if (targetTypeInst == curTypeInst)
|
|
{
|
|
callTarget = target;
|
|
}
|
|
else if (target)
|
|
{
|
|
bool handled = false;
|
|
if (target.mType->IsCompositeType())
|
|
{
|
|
//BF_FATAL("Not implemented");
|
|
callTarget = target;
|
|
/*if (curTypeInst->IsObject())
|
|
{
|
|
// Box it
|
|
callTarget = mModule->Cast(targetSrc, target, curTypeInst, true);
|
|
handled = true;
|
|
}*/
|
|
}
|
|
/*else
|
|
target = mModule->LoadValue(target);*/
|
|
if (!handled)
|
|
{
|
|
/*Value* targetValue = target.mValue;
|
|
callTarget = DbgTypedValue(mModule->mIRBuilder->CreateBitCast(targetValue, curTypeInst->mPtrLLVMType), curTypeInst);*/
|
|
callTarget = target;
|
|
}
|
|
}
|
|
|
|
if (prevReferenceId.mPrevVal != NULL)
|
|
{
|
|
if (prevReferenceId.mPrevVal->IsEmpty())
|
|
*prevReferenceId.mPrevVal += ".";
|
|
*prevReferenceId.mPrevVal += methodDef->ToString();
|
|
}
|
|
|
|
return CreateCall(targetSrc, callTarget, methodDef, bypassVirtual, arguments, argValues);
|
|
}
|
|
|
|
void DbgExprEvaluator::DoInvocation(BfAstNode* target, BfSizedArray<ASTREF(BfExpression*)>& args, BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments)
|
|
{
|
|
bool allowImplicitThis = false;
|
|
BfAstNode* methodNodeSrc = target;
|
|
|
|
bool bypassVirtual = false;
|
|
String targetFunctionName;
|
|
DbgTypedValue thisValue;
|
|
//TODO: This may just be a fully qualified static method name, so let's check that also
|
|
if (auto memberRefExpression = BfNodeDynCast<BfMemberReferenceExpression>(target))
|
|
{
|
|
//GetAutoComplete()->CheckMemberReference(memberRefExpression->mTarget, memberRefExpression->mDotToken, memberRefExpression->mMemberName);
|
|
|
|
if (memberRefExpression->mMemberName == NULL)
|
|
return;
|
|
|
|
if (memberRefExpression->IsA<BfBaseExpression>())
|
|
bypassVirtual = true;
|
|
|
|
methodNodeSrc = memberRefExpression->mMemberName;
|
|
|
|
if (auto attrIdentifier = BfNodeDynCast<BfAttributedIdentifierNode>(memberRefExpression->mMemberName))
|
|
{
|
|
methodNodeSrc = attrIdentifier->mIdentifier;
|
|
if (attrIdentifier->mIdentifier != NULL)
|
|
targetFunctionName = attrIdentifier->mIdentifier->ToString();
|
|
}
|
|
else
|
|
targetFunctionName = memberRefExpression->mMemberName->ToString();
|
|
|
|
if (memberRefExpression->mTarget == NULL)
|
|
{
|
|
// Dot-qualified
|
|
if ((mExpectingType != NULL) && (mExpectingType->IsStruct()))
|
|
{
|
|
mResult = DbgTypedValue();
|
|
mResult.mType = mExpectingType;
|
|
}
|
|
else
|
|
Fail("Unqualified dot syntax can only be used when the result type can be inferred", memberRefExpression->mDotToken);
|
|
}
|
|
else if (auto typeRef = BfNodeDynCast<BfTypeReference>(memberRefExpression->mTarget))
|
|
{
|
|
// Static method
|
|
mResult = DbgTypedValue();
|
|
mResult.mType = ResolveTypeRef(typeRef);
|
|
}
|
|
else
|
|
VisitChild(memberRefExpression->mTarget);
|
|
GetResult();
|
|
if (mResult.mType == NULL)
|
|
return;
|
|
thisValue = mResult;
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else if (auto qualifiedName = BfNodeDynCast<BfQualifiedNameNode>(target))
|
|
{
|
|
/*if (GetAutoComplete() != NULL)
|
|
GetAutoComplete()->CheckMemberReference(qualifiedName->mLeft, qualifiedName->mDot, qualifiedName->mRight);*/
|
|
|
|
String leftName = qualifiedName->mLeft->ToString();
|
|
if (leftName == "base")
|
|
bypassVirtual = true;
|
|
|
|
methodNodeSrc = qualifiedName->mRight;
|
|
if (auto attrIdentifier = BfNodeDynCast<BfAttributedIdentifierNode>(qualifiedName->mRight))
|
|
{
|
|
methodNodeSrc = attrIdentifier->mIdentifier;
|
|
targetFunctionName = attrIdentifier->mIdentifier->ToString();
|
|
}
|
|
else
|
|
targetFunctionName = qualifiedName->mRight->ToString();
|
|
|
|
bool hadError = false;
|
|
thisValue = LookupIdentifier(qualifiedName->mLeft, true, &hadError);
|
|
if (!thisValue)
|
|
thisValue = GetResult();
|
|
if (hadError)
|
|
return;
|
|
if (!thisValue)
|
|
{
|
|
// Identifier not found. Static method? Just check speculatively don't throw error
|
|
DbgType* type;
|
|
{
|
|
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, true);
|
|
type = ResolveTypeRef(qualifiedName->mLeft);
|
|
}
|
|
|
|
/*if (type->IsGenericParam())
|
|
{
|
|
auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)type);
|
|
type = genericParamInstance->mTypeConstraint;
|
|
}*/
|
|
|
|
if (type != NULL)
|
|
thisValue.mType = type;
|
|
else if (auto qualifiedLeft = BfNodeDynCast<BfQualifiedNameNode>(qualifiedName->mLeft))
|
|
{
|
|
LookupQualifiedStaticField(qualifiedLeft, true);
|
|
thisValue = mResult;
|
|
mResult = DbgTypedValue();
|
|
}
|
|
thisValue.mHasNoValue = true;
|
|
}
|
|
if (!thisValue.mType)
|
|
{
|
|
Fail("Identifier not found", qualifiedName->mLeft);
|
|
return;
|
|
}
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else if (auto identiferExpr = BfNodeDynCast<BfIdentifierNode>(target))
|
|
{
|
|
allowImplicitThis = true;
|
|
|
|
targetFunctionName = target->ToString();
|
|
|
|
}
|
|
else if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(target))
|
|
{
|
|
// It appears we're attempting to invoke a delegate returned from another method
|
|
Visit(invocationExpr);
|
|
auto innerInvocationResult = mResult;
|
|
mResult = DbgTypedValue();
|
|
|
|
if (!innerInvocationResult)
|
|
return;
|
|
|
|
/*if (innerInvocationResult.mType->IsTypeInstance())
|
|
{
|
|
auto invocationTypeInst = innerInvocationResult.mType->ToTypeInstance();
|
|
if (invocationTypeInst->mTypeDef->mIsDelegate)
|
|
{
|
|
thisValue = innerInvocationResult;
|
|
targetFunctionName = "Invoke";
|
|
}
|
|
}*/
|
|
|
|
if (!thisValue)
|
|
{
|
|
Fail(StrFormat("Cannot perform invocation on type '%s'", innerInvocationResult.mType->ToString().c_str()), invocationExpr->mTarget);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fail("Invalid invocation target", target);
|
|
return;
|
|
}
|
|
|
|
if (thisValue)
|
|
{
|
|
if (thisValue.mType->IsPointer())
|
|
{
|
|
//BF_ASSERT(thisValue.mIsAddr);
|
|
thisValue.mType = thisValue.mType->mTypeParam;
|
|
thisValue.mSrcAddress = thisValue.mPtr;
|
|
}
|
|
|
|
if (thisValue.mType->IsPrimitiveType())
|
|
{
|
|
//TODO:
|
|
/*auto primStructType = GetPrimitiveStructType(thisValue.mType);
|
|
//thisValue = mModule->Cast(target, thisValue, primStructType);
|
|
|
|
auto srcAlloca = mModule->GetHeadIRBuilder()->CreateAlloca(primStructType->mInstLLVMType, 0);
|
|
srcAlloca->setAlignment(primStructType->mAlign);
|
|
auto primPtr = mModule->mIRBuilder->CreateConstInBoundsGEP2_32(srcAlloca, 0, 0);
|
|
mModule->mIRBuilder->CreateStore(thisValue.mValue, primPtr);
|
|
|
|
thisValue = DbgTypedValue(srcAlloca, primStructType, true);*/
|
|
}
|
|
|
|
if (thisValue.mType->IsEnum())
|
|
{
|
|
//auto enumStructType = thisValue.mType->ToTypeInstance();
|
|
|
|
/*auto srcAlloca = mModule->GetHeadIRBuilder()->CreateAlloca(enumStructType->mInstLLVMType, 0);
|
|
srcAlloca->setAlignment(enumStructType->mAlign);
|
|
auto enumPtr = mModule->mIRBuilder->CreateConstInBoundsGEP2_32(srcAlloca, 0, 0);
|
|
mModule->mIRBuilder->CreateStore(thisValue.mValue, enumPtr);*/
|
|
|
|
//thisValue = DbgTypedValue(mModule->mIRBuilder->CreateBitCast(srcAlloca, enumStructType->mBaseType->mPtrLLVMType), enumStructType->mBaseType, true);
|
|
}
|
|
|
|
/*if (!thisValue.mType->IsTypeInstance())
|
|
{
|
|
Fail(StrFormat("Invalid target type: '%s'", mModule->TypeToString(thisValue.mType).c_str()), target);
|
|
return;
|
|
}*/
|
|
}
|
|
|
|
mResult = MatchMethod(methodNodeSrc, thisValue, allowImplicitThis, bypassVirtual, targetFunctionName, args, methodGenericArguments);
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfInvocationExpression* invocationExpr)
|
|
{
|
|
mIsComplexExpression = true;
|
|
|
|
auto wasCapturingMethodInfo = (mAutoComplete != NULL) && (mAutoComplete->mIsCapturingMethodMatchInfo);
|
|
/*if (mAutoComplete != NULL)
|
|
mAutoComplete->CheckInvocation(invocationExpr, invocationExpr->mOpenParen, invocationExpr->mCloseParen, invocationExpr->mCommas);*/
|
|
|
|
BfSizedArray<ASTREF(BfAstNode*)>* methodGenericArguments = NULL;
|
|
if (invocationExpr->mGenericArgs != NULL)
|
|
methodGenericArguments = &invocationExpr->mGenericArgs->mGenericArgs;
|
|
DoInvocation(invocationExpr->mTarget, invocationExpr->mArguments, methodGenericArguments);
|
|
|
|
if ((wasCapturingMethodInfo) && (!mAutoComplete->mIsCapturingMethodMatchInfo))
|
|
{
|
|
mAutoComplete->mIsCapturingMethodMatchInfo = true;
|
|
BF_ASSERT(mAutoComplete->mMethodMatchInfo != NULL);
|
|
}
|
|
else if (mAutoComplete != NULL)
|
|
mAutoComplete->mIsCapturingMethodMatchInfo = false;
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfConditionalExpression* condExpr)
|
|
{
|
|
VisitChild(condExpr->mConditionExpression);
|
|
GetResult();
|
|
if (!mResult)
|
|
return;
|
|
|
|
auto boolResult = Cast(condExpr->mConditionExpression, mResult, mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()));
|
|
if (!boolResult)
|
|
return;
|
|
|
|
if (boolResult.mBool)
|
|
{
|
|
if (condExpr->mTrueExpression != NULL)
|
|
VisitChild(condExpr->mTrueExpression);
|
|
else
|
|
mResult = DbgTypedValue();
|
|
}
|
|
else
|
|
{
|
|
if (condExpr->mFalseExpression != NULL)
|
|
VisitChild(condExpr->mFalseExpression);
|
|
else
|
|
mResult = DbgTypedValue();
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfTypeAttrExpression* typeAttrExpr)
|
|
{
|
|
if (typeAttrExpr->mTypeRef == NULL)
|
|
return;
|
|
|
|
AutocompleteCheckType(typeAttrExpr->mTypeRef);
|
|
auto dbgType = ResolveTypeRef(typeAttrExpr->mTypeRef);
|
|
if (dbgType == NULL)
|
|
return;
|
|
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage());
|
|
switch (typeAttrExpr->mToken->GetToken())
|
|
{
|
|
case BfToken_SizeOf:
|
|
mResult.mInt64 = dbgType->GetByteCount();
|
|
break;
|
|
case BfToken_AlignOf:
|
|
mResult.mInt64 = dbgType->GetAlign();
|
|
break;
|
|
case BfToken_StrideOf:
|
|
mResult.mInt64 = dbgType->GetStride();
|
|
break;
|
|
default:
|
|
Fail("Invalid attribute expression", typeAttrExpr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DbgExprEvaluator::Visit(BfTupleExpression* tupleExpr)
|
|
{
|
|
if (mReceivingValue == NULL)
|
|
{
|
|
Fail("Tuple expressions can only be used when the receiving address can be inferred", tupleExpr);
|
|
return;
|
|
}
|
|
|
|
if (mReceivingValue->mIsReadOnly)
|
|
{
|
|
Fail("Cannot write to read-only value", tupleExpr);
|
|
return;
|
|
}
|
|
|
|
auto type = mReceivingValue->mType->RemoveModifiers();
|
|
if (!type->IsBfTuple())
|
|
{
|
|
Fail(StrFormat("Tuple expressions cannot be used for type '%s'", mReceivingValue->mType->ToString().c_str()), tupleExpr);
|
|
return;
|
|
}
|
|
|
|
CheckTupleCreation(mReceivingValue->mSrcAddress, tupleExpr, mReceivingValue->mType, tupleExpr->mValues, &tupleExpr->mNames);
|
|
mReceivingValue = NULL;
|
|
}
|
|
|
|
DbgTypedValue DbgExprEvaluator::Resolve(BfExpression* expr, DbgType* wantType)
|
|
{
|
|
//BfLogDbgExpr("Dbg Evaluate %s\n", expr->ToString().c_str());
|
|
|
|
BF_ASSERT(!HasPropResult());
|
|
|
|
mCallStackPreservePos = 0;
|
|
|
|
SetAndRestoreValue<DbgType*> prevType(mExpectingType, wantType);
|
|
SetAndRestoreValue<DbgTypedValue> prevResult(mResult, DbgTypedValue());
|
|
|
|
if ((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0)
|
|
{
|
|
BF_ASSERT(mCallResults != NULL);
|
|
}
|
|
|
|
if (mExplicitThis)
|
|
mExplicitThis = FixThis(mExplicitThis);
|
|
|
|
mExpectingType = wantType;
|
|
VisitChildNoRef(expr);
|
|
GetResult();
|
|
while (true)
|
|
{
|
|
if (!mResult)
|
|
break;
|
|
if (//(mResult.mType->mTypeCode == DbgType_Const) ||
|
|
(mResult.mType->mTypeCode == DbgType_Volatile))
|
|
mResult.mType = mResult.mType->mTypeParam;
|
|
else
|
|
break;
|
|
}
|
|
|
|
if ((mResult) && (wantType != NULL))
|
|
mResult = Cast(expr, mResult, wantType);
|
|
|
|
if ((mReferenceId != NULL) && (mIsComplexExpression))
|
|
mReferenceId->clear();
|
|
|
|
return mResult;
|
|
}
|
|
|
|
BfAstNode* DbgExprEvaluator::FinalizeExplicitThisReferences(BfAstNode* headNode)
|
|
{
|
|
//return headNode;
|
|
if (mExplicitThisExpr == NULL)
|
|
return headNode;
|
|
auto explicitThisExpr = mExplicitThisExpr;
|
|
mExplicitThisExpr = NULL;
|
|
|
|
BfReducer bfReducer;
|
|
|
|
BfPrinter bfPrinter(explicitThisExpr->GetSourceData()->mRootNode, NULL, NULL);
|
|
bfPrinter.mIgnoreTrivia = true;
|
|
bfPrinter.mReformatting = true;
|
|
bfPrinter.VisitChild(explicitThisExpr);
|
|
|
|
bool doWrap = false;
|
|
String thisExprStr = bfPrinter.mOutString;
|
|
|
|
if (auto unaryOpExpr = BfNodeDynCast<BfUnaryOperatorExpression>(explicitThisExpr))
|
|
{
|
|
if ((unaryOpExpr->mOp != BfUnaryOp_AddressOf) && (unaryOpExpr->mOp != BfUnaryOp_Dereference))
|
|
doWrap = true;
|
|
}
|
|
if ((explicitThisExpr->IsA<BfCastExpression>()) ||
|
|
(explicitThisExpr->IsA<BfBinaryOperatorExpression>()) ||
|
|
(explicitThisExpr->IsA<BfUnaryOperatorExpression>()))
|
|
doWrap = true;
|
|
if (doWrap)
|
|
{
|
|
thisExprStr = "(" + thisExprStr + ")";
|
|
}
|
|
|
|
auto bfParser = headNode->GetSourceData()->ToParser();
|
|
BF_ASSERT(bfParser != NULL);
|
|
|
|
auto source = headNode->GetSourceData();
|
|
for (auto& replaceNodeRecord : mDeferredInsertExplicitThisVector)
|
|
{
|
|
auto replaceNode = replaceNodeRecord.mNode;
|
|
|
|
DbgType* castType = NULL;
|
|
|
|
auto typeRef = BfNodeDynCast<BfTypeReference>(replaceNode);
|
|
if ((typeRef != NULL) || (replaceNodeRecord.mForceTypeRef))
|
|
{
|
|
castType = ResolveTypeRef(replaceNode);
|
|
if (castType == NULL)
|
|
continue;
|
|
}
|
|
|
|
// Use an identifier as a text placeholder for the whole 'this' expression
|
|
// We must do this because we can't insert the actual 'this' node into multiple parents
|
|
auto thisExprNode = source->mAlloc.Alloc<BfIdentifierNode>();
|
|
BfAstNode* newNode = NULL;
|
|
|
|
if (castType != NULL)
|
|
{
|
|
bfReducer.ReplaceNode(replaceNode, thisExprNode);
|
|
newNode = thisExprNode;
|
|
}
|
|
else if (auto thisNode = BfNodeDynCast<BfThisExpression>(replaceNode))
|
|
{
|
|
bfReducer.ReplaceNode(replaceNode, thisExprNode);
|
|
newNode = thisExprNode;
|
|
}
|
|
else if (auto identifierNode = BfNodeDynCast<BfIdentifierNode>(replaceNode))
|
|
{
|
|
auto memberRefNode = source->mAlloc.Alloc<BfMemberReferenceExpression>();
|
|
bfReducer.ReplaceNode(replaceNode, memberRefNode);
|
|
memberRefNode->mMemberName = identifierNode;
|
|
|
|
auto tokeNode = source->mAlloc.Alloc<BfTokenNode>();
|
|
tokeNode->SetToken(BfToken_Dot);
|
|
bfReducer.MoveNode(tokeNode, memberRefNode);
|
|
memberRefNode->mDotToken = tokeNode;
|
|
|
|
bfReducer.MoveNode(thisExprNode, memberRefNode);
|
|
memberRefNode->mTarget = thisExprNode;
|
|
|
|
newNode = memberRefNode;
|
|
}
|
|
|
|
if (newNode != NULL)
|
|
{
|
|
if (replaceNodeRecord.mNodeRef != NULL)
|
|
*replaceNodeRecord.mNodeRef = newNode;
|
|
if (replaceNode == headNode)
|
|
headNode = newNode;
|
|
}
|
|
|
|
String replaceString;
|
|
if (castType != NULL)
|
|
{
|
|
replaceString = castType->ToString();
|
|
}
|
|
else
|
|
replaceString = thisExprStr;
|
|
|
|
int thisLen = (int)replaceString.length();
|
|
int srcStart = bfParser->AllocChars(thisLen);
|
|
memcpy((char*)source->mSrc + srcStart, replaceString.c_str(), thisLen);
|
|
thisExprNode->Init(srcStart, srcStart, srcStart + thisLen);
|
|
}
|
|
|
|
return headNode;
|
|
}
|