1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/IDEHelper/DbgExprEvaluator.cpp
Brian Fiete b63a243fd7 Working on installer, fixing more Win32 issues
Throwing error on member references with ".." cascade token outside invocations (ie: "ts..mA = 123")
Fixed 'Thread.ModuleTLSIndex' error - which caused us TLS lookup failures in Beef DLLs
Fixed some hotswap errors
Made BeefPerf shut down properly
Fixed an 'int literal' FixIntUnknown issue where rhs was System.Object which caused an illegal boxing
Fixed COFF::LocateSymbol issues with Win32 and also with linking to static libraries - showed up with hot-linking in fmod when hot-adding a floating point mod
Fixed a couple memory leaks
Fixed alignment issue in COFF::ParseCompileUnit
2019-09-02 17:39:47 -07:00

8014 lines
220 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(BfTypeReference*)>* 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)
{
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->CanImplicitlyCast(DbgTypedValue(NULL, argType), prevGenericMethodArg))
return true;
// New best?
if (mModule->CanImplicitlyCast(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 = (BfGenericTypeInstance*)wantType;
auto argGenericType = (BfGenericTypeInstance*)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->CanImplicitlyCast(DbgTypedValue::GetValueless(paramType), prevParamType))
isBetter = true;
if (mExprEvaluator->CanImplicitlyCast(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->CanImplicitlyCast(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->CanImplicitlyCast(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->CanImplicitlyCast(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->mIsHotObjectFile)
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;
mDebugTarget = dbgModule->mDebugTarget;
mOrigDbgModule = dbgModule;
mDbgModule = dbgModule->GetLinkedModule();
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;
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;
}
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<BfDeclTypeRef>(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();
auto typeTypeEntry = mDebugTarget->mTargetBinary->FindType("System.Type", DbgLanguage_Beef);
if ((typeTypeEntry == NULL) || (typeTypeEntry->mValue == NULL))
return DbgTypedValue();
auto typeType = typeTypeEntry->mValue;
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;
int64 valAddr = member->mCompileUnit->mDbgModule->EvaluateLocation(NULL, member->mLocationData, member->mLocationLen, stackFrame, &addrType);
if (valAddr != 0)
{
DbgTypedValue typedVal;
typedVal.mType = typeType;
addr_target addr = valAddr + typeId * sizeof(addr_target);
typedVal.mSrcAddress = mDebugger->ReadMemory<addr_target>(addr);
return typedVal;
}
}
}
return DbgTypedValue();
}
void DbgExprEvaluator::BeefStringToString(addr_target addr, String& outStr)
{
int objectSize = mDebugTarget->mBfObjectSize;
int32 strLen = mDebugger->ReadMemory<int16>(addr + objectSize);
addr_target charPtr = mDebugger->ReadMemory<addr_target>(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;
struct _Type
{
int32 mSize;
_TypeId mTypeId;
_TypeFlags mTypeFlags;
int32 mMemberDataOffset;
_TypeCode mTypeCode;
uint8 mAlign;
};
struct _String
{
};
struct _MethodData
{
};
struct _FieldData
{
};
struct _ClassVData
{
};
struct _TypeInstance : public _Type
{
_ClassVData* mTypeClassVData;
_String* mName;
_String* mNamespace;
int32 mInstSize;
int32 mInstAlign;
int32 mCustomAttributesIdx;
_TypeId mBaseType;
_TypeId mUnderlyingType;
_TypeId mOuterType;
int32 mInheritanceId;
int32 mInheritanceCount;
uint8 mInterfaceSlot;
uint8 mInterfaceCount;
int16 mMethodDataCount;
int16 mPropertyDataCount;
int16 mFieldDataCount;
int16 mConstructorDataCount;
void* mInterfaceDataPtr;
_MethodData* mMethodDataPtr;
void* mPropertyDataPtr;
_FieldData* mFieldDataPtr;
void* mConstructorDataPtr;
void** mCustomAttrDataPtr;
};
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_Struct) != 0) || ((typeFlags & BfTypeFlags_TypedPrimitive) != 0) || ((typeFlags & BfTypeFlags_Object) != 0))
{
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));
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_Single: 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(valType, valAddr, addrType));
}
if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType))
{
DbgTypedValue result = ReadTypedValue(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::CanImplicitlyCast(DbgTypedValue typedVal, DbgType* toType)
{
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->IsPrimitiveType())
{
if (fromType->mTypeCode == toType->mTypeCode)
return typedVal;
// Typed primitive?
int thisOffset = 0;
addr_target thisVal = typedVal.mSrcAddress;
if (TypeIsSubTypeOf(fromType, toType, &thisOffset, &thisVal))
{
if (typedVal.mSrcAddress == 0)
{
if (fromType->IsTypedPrimitive())
{
DbgTypedValue val = typedVal;
val.mType = toType;
return val;
}
}
DbgTypedValue val = ReadTypedValue(toType, typedVal.mSrcAddress, DbgAddrType_Target);
return val;
}
//TODO: Check to see if other types are equal as well
}
/*if (toType->IsBfObject())
toType = mDbgModule->GetPointerType(toType);*/
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;
}
/*if ((fromType->IsConst()) || (toType->IsConst()))
{
auto checkFromType = fromType;
if (checkFromType->IsConst())
checkFromType = checkFromType->mTypeParam;
auto checkToType = toType;
if (checkToType->IsConst())
checkToType = checkToType->mTypeParam;
DbgTypedValue checkVal = typedVal;
checkVal.mType = checkFromType;
auto result = Cast(srcNode, checkVal, checkToType, explicitCast, true);
if (result)
{
result.mType = toType;
return result;
}
}*/
/*if ((fromType->IsPointer()) && (toType->IsCompositeType()))
{
DbgType* toPtrType = mDbgModule->GetPointerType(toType);
DbgTypedValue val;
val.mPtr = typedVal.mPtr;
val.mType = toPtrType;
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(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)
{
AutoPerf perf("DbgExprEvaluator::DoLookupField");
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;
int64 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(checkMember->mType, valAddr, addrType);
}
else if (checkMember->mIsConst)
{
return ReadTypedValue(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(checkMember->mType, (uint64)&target.mPtr + checkMember->mMemberOffset, DbgAddrType_Local);
}
else
{
if (target.mType->HasPointer())
targetPtr = target.mPtr;
typedValue = ReadTypedValue(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;
}
if (((!method->mHasThis) && (wantsStatic)) ||
((method->mHasThis) && (!wantsStatic || allowImplicitThis)))
{
if ((method->mHasThis) && (allowImplicitThis) && (!target))
{
target = GetThis();
}
DbgSubprogram*& subprogramRef = isGetter ? mPropGet : mPropSet;
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)
{
AutoPerf perf("DbgExprEvaluator::LookupField");
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(DbgType* dbgType, uint64 valAddr, DbgAddrType addrType)
{
bool failedReadMemory = false;
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(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(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->GetRootBaseType();
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(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)
{
AutoPerf perf("DbgExprEvaluator::LookupIdentifier");
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[0] == '$')
{
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)
return GetRegister(findName.Substring(1));
}
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;
{
AutoPerf perf("mDebugTarget->GetValueByName");
mDebugTarget->GetValueByName(GetCurrentMethod(), "*", stackFrame, &valAddr, &valType, &addrType);
}
mDebugTarget->mCapturedNamesPtr = NULL;
mDebugTarget->mCapturedTypesPtr = NULL;
auto language = GetLanguage();
BF_ASSERT(capturedTypes.size() == capturedNames.size());
{
AutoPerf perf("capturedNames Add");
//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
AutoPerf perf("GetValueByName __lambda");
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)
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)
{
AutoPerf perf("GetValueByName findName");
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(valType, valAddr, addrType);
}
}
}
bool isClosure = false;
if (language == DbgLanguage_Beef)
{
AutoPerf perf("GetValueByName __closure");
intptr valAddr;
DbgType* valType;
//bool valIsAddr = false;
if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType))
{
DbgTypedValue closureValue = ReadTypedValue(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);
// }
// }
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;
AutoPerf perf("DbgExprEvaluator::AutocompleteAddMembers");
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];
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(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;
}
}
auto memberType = collection.mType->mTypeParam;
auto result = ReadTypedValue(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(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(valType, valAddr, addrType);
return;
}
}
}
}
void DbgExprEvaluator::LookupSplatMember(BfAstNode* targetNode, BfAstNode* lookupNode, const DbgTypedValue& target, const StringImpl& fieldName, String* outFindName, bool* outIsConst)
{
/*DbgVariable* dbgVariable = (DbgVariable*)target.mVariable;
if (dbgVariable != NULL)
return LookupSplatMember(target, fieldName);*/
while (auto parenNode = BfNodeDynCast<BfParenthesizedExpression>(lookupNode))
lookupNode = parenNode->mExpression;
SplatLookupEntry* splatLookupEntry = NULL;
if (lookupNode != NULL)
{
if (mSplatLookupMap.TryAdd(lookupNode, NULL, &splatLookupEntry))
{
//
}
else
{
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;
}
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(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType))
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(GetCurrentMethod(), 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(memberType, valAddr, addrType);
if (wantConst)
{
// Set readonly if the original value was const
mResult.mIsReadOnly = true;
}
}
}
else
{
if (target.mSrcAddress == -1)
{
if (!memberType->IsStruct())
Fail("Failed to lookup splat member", (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_Single:
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 ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_InEquality))
{
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);
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()) && ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_InEquality)))
{
bool isEquality = (binaryOp == BfBinaryOp_Equality);
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_InEquality) &&
(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 ((binaryOp != BfBinaryOp_Equality) && (binaryOp != BfBinaryOp_InEquality))
{
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) && (!isCompare)))
{
Fail("Can only add or subtract integer values from pointers", rightExpression);
return;
}
if ((binaryOp == BfBinaryOp_Add) || (binaryOp == BfBinaryOp_Subtract))
{
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 ((binaryOp != BfBinaryOp_Equality) && (binaryOp != BfBinaryOp_InEquality))
{
Fail("Invalid operation for objects", opToken);
return;
}
if (otherType->IsNull())
{
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
if (binaryOp == BfBinaryOp_Equality)
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)
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 (CanImplicitlyCast(*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:
mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage());
mResult.mBool = true;
break;
case BfBinaryOp_InEquality:
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:
mResult.mBool = convLeftValue.mBool == convRightValue.mBool;
break;
case BfBinaryOp_InEquality:
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:
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:
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:
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:
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:
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(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 != boolType)
{
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, 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();
}*/
int curCallResultIdx = mCallResultIdx++;
if (((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) == 0) || (mCreatedPendingCall))
{
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)
{
return (*mCallResults)[curCallResultIdx].mResult;
}
else if (curCallResultIdx == (int)mCallResults->size() - 1)
{
CPURegisters newPhysRegisters;
mDebugger->PopulateRegisters(&newPhysRegisters);
DbgTypedValue returnVal;
if (method->mReturnType != 0)
{
returnVal = mDebugger->ReadReturnValue(&newPhysRegisters, method->mReturnType);
bool hadRef = false;
auto returnType = method->mReturnType->RemoveModifiers(&hadRef);
if (hadRef)
{
returnVal = ReadTypedValue(returnType, returnVal.mPtr, DbgAddrType_Target);
returnVal.mType = returnType;
}
}
else
{
returnVal.mType = mDbgModule->GetPrimitiveType(DbgType_Void, GetLanguage());
}
mDebugger->RestoreAllRegisters();
(*mCallResults)[curCallResultIdx].mResult = returnVal;
return returnVal;
}
// 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;
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 (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(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);
};
int methodParamCount = method->mParams.Size();
if (method->mHasThis)
{
auto param = method->mParams[paramIdx];
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->CanImplicitlyCast(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)
{
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(&registers);
auto* regSP = registers.GetSPRegisterRef();
int paramIdx = argPushQueue.size() - 1;
if (method->mHasThis)
paramIdx--;
DbgTypedValue compositeRetVal;
if (mDebugger->CheckNeedsSRetArgument(method->mReturnType))
{
int retSize = BF_ALIGN(method->mReturnType->GetByteCount(), 16);
*regSP -= retSize;
compositeRetVal.mType = method->mReturnType;
compositeRetVal.mSrcAddress = *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 (compositeRetVal.mSrcAddress != 0)
{
mDebugger->AddParamValue(0, method->mHasThis, &registers, compositeRetVal);
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(&registers, thisAddr);
}
else
{
mDebugger->AddParamValue(paramIdx, method->mHasThis, &registers, 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, bypassVirtual, &registers);
}
DbgTypedValue DbgExprEvaluator::MatchMethod(BfAstNode* targetSrc, DbgTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& methodName,
const BfSizedArray<ASTREF(BfExpression*)>& arguments, BfSizedArray<ASTREF(BfTypeReference*)>* 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());
if (castedType == NULL)
{
if (typeName.EndsWith('*'))
{
auto noPtrTypeEntry = mDbgModule->FindType(typeName.Substring(0, typeName.length() - 1), NULL, DbgLanguage_Beef);
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() == 1)
{
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 == "__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 = (int)argValues[1].GetInt64();
return argValues[0];
}
}
else if (methodName == "__getHighBits")
{
if ((argValues.size() == 2) && (argValues[0]) && (argValues[1]))
{
uint64 val = (uint64)argValues[0].GetInt64();
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 = (uint64)argValues[0].GetInt64();
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;
}
}
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(BfTypeReference*)>* 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 (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(BfTypeReference*)>* 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;
case BfToken_TypeOf:
//TODO:
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)
{
AutoPerf perf3("DbgExprEvaluator::Resolve");
BF_ASSERT(!HasPropResult());
SetAndRestoreValue<DbgType*> prevType(mExpectingType, wantType);
SetAndRestoreValue<DbgTypedValue> prevResult(mResult, DbgTypedValue());
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;
}