#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& arguments, BfSizedArray* 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->CanCast(DbgTypedValue(NULL, argType), prevGenericMethodArg)) return true; // New best? if (mModule->CanCast(DbgTypedValue(NULL, prevGenericMethodArg), argType)) { mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = argType; return true; } // No implicit conversion, FAIL! mCheckMethodGenericArguments[wantGenericParam->mGenericParamIdx] = NULL; return false; } return true; } if (wantType->IsGenericTypeInstance()) { if (!argType->IsGenericTypeInstance()) return true; auto wantGenericType = (BfTypeInstance*)wantType; auto argGenericType = (BfTypeInstance*)argType; if (argGenericType->mTypeDef != wantGenericType->mTypeDef) return true; for (int genericArgIdx = 0; genericArgIdx < (int)argGenericType->mTypeGenericArguments.size(); genericArgIdx++) InferGenericArgument(argGenericType->mTypeGenericArguments[genericArgIdx], wantGenericType->mTypeGenericArguments[genericArgIdx]); return true; } if (wantType->IsRef()) { if (!argType->IsRef()) return true; auto wantRefType = (BfRefType*)wantType; auto argRefType = (BfRefType*)argType; if (wantRefType->mIsOut != argRefType->mIsOut) return true; InferGenericArgument(argRefType->mElementType, wantRefType->mElementType); return true; } if (wantType->IsPointer()) { if (!argType->IsPointer()) return true; auto wantPointerType = (BfPointerType*) wantType; auto argPointerType = (BfPointerType*) argType; InferGenericArgument(argPointerType->mElementType, wantPointerType->mElementType); return true; } return true; }*/ void DwMethodMatcher::CompareMethods(DbgSubprogram* prevMethodInstance, DwTypeVector* prevGenericArgumentsSubstitute, DbgSubprogram* newMethodInstance, DwTypeVector* genericArgumentsSubstitute, bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail) { int numUsedParams = 0; int prevNumUsedParams = 0; bool usedExtendedForm = false; bool prevUsedExtendedForm = false; bool isBetter = false; bool isWorse = false; int argIdx = 0; DbgSubprogram* prevMethodDef = prevMethodInstance; DbgSubprogram* newMethodDef = newMethodInstance; #define SET_BETTER_OR_WORSE(lhs, rhs) \ if ((lhs) && !(rhs)) isBetter = true; \ if (!(lhs) && (rhs)) isWorse = true; int newArgOffset = newMethodInstance->mHasThis ? 1 : 0; int prevArgOffset = prevMethodInstance->mHasThis ? 1 : 0; DbgVariable* param = newMethodInstance->mParams.mHead; if (newMethodInstance->mHasThis) param = param->mNext; DbgVariable* prevParam = prevMethodInstance->mParams.mHead; if (prevMethodInstance->mHasThis) prevParam = prevParam->mNext; for (argIdx = 0; argIdx < (int) mArguments.size(); argIdx++) { DbgTypedValue arg = mArguments[argIdx]; DbgType* paramType = param->mType; DbgType* prevParamType = prevParam->mType; numUsedParams++; prevNumUsedParams++; /*if ((genericArgumentsSubstitute != NULL) && (paramType->IsUnspecializedType())) paramType = mModule->SpecializeMethodType(paramType, *genericArgumentsSubstitute, allowSpecializeFail); if (prevParamType->IsUnspecializedType()) prevParamType = mModule->SpecializeMethodType(prevParamType, *prevGenericArgumentsSubstitute, allowSpecializeFail);*/ //paramType = paramType->RemoveModifiers(); //prevParamType = prevParamType->RemoveModifiers(); if (paramType != prevParamType) { /*if ((paramType->IsGenericParam()) || (prevParamType->IsGenericParam())) { BF_ASSERT(allowSpecializeFail); if (!prevParamType->IsGenericParam()) isBetter = true; else if (!paramType->IsGenericParam()) isWorse = true; } else*/ if ((paramType->IsInteger()) && (prevParamType->IsInteger())) { if (paramType == arg.mType) isBetter = true; else if (prevParamType == arg.mType) isWorse = true; else { if (paramType->mSize < prevParamType->mSize) isBetter = true; else if (paramType->mSize > prevParamType->mSize) isWorse = true; else if (paramType->IsSigned()) isBetter = true; else isWorse = true; } } else { if (mExprEvaluator->CanCast(DbgTypedValue::GetValueless(paramType), prevParamType)) isBetter = true; if (mExprEvaluator->CanCast(DbgTypedValue::GetValueless(prevParamType), paramType)) isWorse = true; } } /*if (newMethodDef->mParams[argIdx]->mParamType == BfParamType_Params) usedExtendedForm = true; if (prevMethodDef->mParams[argIdx]->mParamType == BfParamType_Params) prevUsedExtendedForm = true;*/ if ((usedExtendedForm) || (prevUsedExtendedForm)) break; param = param->mNext; prevParam = prevParam->mNext; } // Check for unused extended params as next param - that still counts as using extended form /*if ((argIdx < (int) newMethodDef->mParams.size()) && (newMethodDef->mParams[argIdx]->mParamType == BfParamType_Params)) usedExtendedForm = true; if ((argIdx < (int) prevMethodDef->mParams.size()) && (prevMethodDef->mParams[argIdx]->mParamType == BfParamType_Params)) prevUsedExtendedForm = true;*/ // Non-generic trumps generic /*if ((!isBetter) && (!isWorse)) { SET_BETTER_OR_WORSE(newMethodInstance->mMethodGenericArguments.size() == 0, prevMethodInstance->mMethodGenericArguments.size() == 0); }*/ // Normal form trumps extended form if ((!isBetter) && (!isWorse)) { SET_BETTER_OR_WORSE(!usedExtendedForm, !prevUsedExtendedForm); } // More used params trumps less params if ((!isBetter) && (!isWorse)) { int paramDiff = (int) numUsedParams - (int) prevNumUsedParams; SET_BETTER_OR_WORSE(paramDiff > 0, paramDiff < 0); } // Fewer defaults trumps more defaults /*if ((!isBetter) && (!isWorse)) { // Since we know the number of used params is the same (previous check), we infer that the rest are defaults int paramDiff = (int) newMethodInstance->mParamTypes.size() - (int) prevMethodInstance->mParamTypes.size(); SET_BETTER_OR_WORSE(paramDiff < 0, paramDiff > 0); }*/ // Check specificity of args if ((!isBetter) && (!isWorse)) { param = newMethodInstance->mParams.mHead; if (newMethodInstance->mHasThis) param = param->mNext; prevParam = prevMethodInstance->mParams.mHead; if (prevMethodInstance->mHasThis) prevParam = prevParam->mNext; for (argIdx = 0; argIdx < (int) mArguments.size(); argIdx++) { DbgType* paramType = param->mType; DbgType* prevParamType = prevParam->mType; //TODO: //isBetter |= mModule->IsTypeMoreSpecific(paramType, prevParamType); //isWorse |= mModule->IsTypeMoreSpecific(prevParamType, paramType); param = param->mNext; prevParam = prevParam->mNext; } } // Prefer virtual - this helps us bind to a virtual override instead of the concrete method implementation so we can ignore it and // continue searching through the base types to find the original virtual method if ((!isBetter) && (!isWorse)) { SET_BETTER_OR_WORSE(newMethodInstance->mVirtual, prevMethodInstance->mVirtual); } if ((!isBetter) && (!isWorse)) { SET_BETTER_OR_WORSE(newMethodInstance->mBlock.mLowPC != 0, prevMethodInstance->mBlock.mLowPC != 0); } if ((!isBetter) && (!isWorse)) { if ((newMethodInstance->mHasThis) && (prevMethodInstance->mHasThis)) { SET_BETTER_OR_WORSE( newMethodInstance->mParams.mHead->mType->IsConst() == mTargetIsConst, prevMethodInstance->mParams.mHead->mType->IsConst() == mTargetIsConst); } } *outNewIsBetter = isBetter; *outNewIsWorse = isWorse; } bool DwMethodMatcher::CheckMethod(DbgType* typeInstance, DbgSubprogram* checkMethod) { bool hadMatch = false; checkMethod->PopulateSubprogram(); // Never consider overrides - they only get found at original method declaration /*if ((checkMethod->mVirtual) && (checkMethod->mVTableLoc == -1)) return true;*/ //BfMethodInstance* methodInstance = mModule->GetRawMethodInstanceAtIdx(typeInstance, checkMethod->mIdx); DbgSubprogram* methodInstance = checkMethod; DwAutoComplete* autoComplete = mExprEvaluator->mAutoComplete; if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo)) { DwAutoComplete::MethodMatchEntry methodMatchEntry; methodMatchEntry.mDwSubprogram = methodInstance; autoComplete->mMethodMatchInfo->mInstanceList.push_back(methodMatchEntry); } /*if ((mHadExplicitGenericArguments) && (checkMethod->mGenericParams.size() != mBestMethodGenericArguments.size())) goto NoMatch;*/ for (auto& checkGenericArgRef : mCheckMethodGenericArguments) checkGenericArgRef = NULL; /*mCheckMethodGenericArguments.resize(checkMethod->mGenericParams.size()); for (auto& genericArgRef : mCheckMethodGenericArguments) genericArgRef = NULL;*/ int argIdx = 0; int paramIdx = 0; DbgType* paramsElementType = NULL; //bool needInferGenericParams = (checkMethod->mGenericParams.size() != 0) && (!mHadExplicitGenericArguments); DwTypeVector* genericArgumentsSubstitute = NULL; /*if (mHadExplicitGenericArguments) genericArgumentsSubstitute = &mBestMethodGenericArguments; else if (needInferGenericParams) genericArgumentsSubstitute = &mCheckMethodGenericArguments; if (needInferGenericParams) { for (int argIdx = 0; argIdx < (int)mArguments.size(); argIdx++) { if (argIdx >= (int)checkMethod->mParams.size()) break; auto wantType = methodInstance->mParamTypes[argIdx]; if (wantType->IsUnspecializedType()) { if (!InferGenericArgument(mArguments[argIdx].mType, wantType)) goto NoMatch; } } for (int checkArgIdx = 0; checkArgIdx < (int) checkMethod->mGenericParams.size(); checkArgIdx++) if (mCheckMethodGenericArguments[checkArgIdx] == NULL) goto NoMatch; }*/ // Iterate through params int paramOffset = checkMethod->mHasThis ? 1 : 0; while (true) { // Too many arguments if (paramIdx >= checkMethod->mParams.Size() - paramOffset) { break; } DbgVariable* paramDef = checkMethod->mParams[paramIdx + paramOffset]; //TODO: /*if ((paramDef->mParamType == BfParamType_Params) && (paramsElementType == NULL)) { if (paramIdx >= (int) mArguments.size()) break; // No params if (!mArguments[argIdx]) goto NoMatch; // Check to see if we're directly passing the params type (like an int[]) auto paramsArrayType = methodInstance->mParamTypes[paramIdx]; if (mModule->CanCast(mArguments[argIdx], paramsArrayType)) { argIdx++; paramIdx++; break; } BF_ASSERT(paramsArrayType->IsArray()); auto arrayType = (BfArrayType*)paramsArrayType; paramsElementType = arrayType->mTypeGenericArguments[0]; while (argIdx < (int)mArguments.size()) { if (!mArguments[argIdx]) goto NoMatch; if (!mModule->CanCast(mArguments[argIdx], paramsElementType)) goto NoMatch; argIdx++; } break; }*/ if (paramIdx >= (int) mArguments.size()) { // We have defaults the rest of the way, so that's cool //TODO: /*if (paramDef->mParamDeclaration->mInitializer != NULL) break;*/ // We have unused params left over goto NoMatch; } auto wantType = paramDef->mType; /*if ((genericArgumentsSubstitute != NULL) && (wantType->IsUnspecializedType())) wantType = mModule->SpecializeMethodType(wantType, *genericArgumentsSubstitute);*/ if (!mArguments[argIdx]) goto NoMatch; if (!mExprEvaluator->CanCast(mArguments[argIdx], wantType)) goto NoMatch; paramIdx++; argIdx++; if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo)) { auto methodMatchInfo = autoComplete->mMethodMatchInfo; if (!methodMatchInfo->mHadExactMatch) { bool isBetter = false; bool isWorse = false; int methodIdx = (int)methodMatchInfo->mInstanceList.size() - 1; if (methodMatchInfo->mBestIdx < (int)methodMatchInfo->mInstanceList.size()) { auto prevMethodMatchEntry = &methodMatchInfo->mInstanceList[methodMatchInfo->mBestIdx]; if (checkMethod->mParams.Size() < (int)mArguments.size()) { isWorse = true; } else if (prevMethodMatchEntry->mDwSubprogram->mParams.Size() < (int) mArguments.size()) { isBetter = true; } else { //BfMethodInstance* prevMethodInstance = mModule->GetRawMethodInstanceAtIdx(prevMethodMatchEntry->mTypeInstance, prevMethodMatchEntry->mMethodDef->mIdx); DbgSubprogram* prevMethodInstance = prevMethodMatchEntry->mDwSubprogram; CompareMethods(prevMethodInstance, &prevMethodMatchEntry->mDwGenericArguments, methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, true); } } if ((argIdx > methodMatchInfo->mMostParamsMatched) || ((argIdx == methodMatchInfo->mMostParamsMatched) && (isBetter)) || ((argIdx == methodMatchInfo->mMostParamsMatched) && (methodIdx == methodMatchInfo->mPrevBestIdx) && (!isWorse))) { methodMatchInfo->mBestIdx = methodIdx; methodMatchInfo->mMostParamsMatched = argIdx; } } } } //TODO: Does this ever get hit? // Not enough arguments? if (argIdx < (int)mArguments.size()) { goto NoMatch; } // Method is applicable, check to see which method is better if (mBestMethodDef != NULL) { bool isBetter = false; bool isWorse = false; //BfMethodInstance* prevMethodInstance = mModule->GetRawMethodInstanceAtIdx(mBestMethodTypeInstance, mBestMethodDef->mIdx); DbgSubprogram* prevMethodInstance = mBestMethodDef; CompareMethods(prevMethodInstance, &mBestMethodGenericArguments, methodInstance, genericArgumentsSubstitute, &isBetter, &isWorse, false); // If we had both a 'better' and 'worse', that's ambiguous because the methods are each better in different ways (not allowed) // And if neither better nor worse then they are equally good, which is not allowed either if (((!isBetter) && (!isWorse)) || ((isBetter) && (isWorse))) { // When we have [Checked] and [Unchecked] both, they will confict here but be equal if (!prevMethodInstance->Equals(methodInstance)) { mExprEvaluator->Fail(StrFormat("Ambiguous method call between '%s' and '%s'", prevMethodInstance->ToString().c_str(), methodInstance->ToString().c_str()), mTargetSrc); } } if (!isBetter) goto Done; } if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo)) { auto methodMatchInfo = autoComplete->mMethodMatchInfo; // Try to persist with previous partial match, if we have one - this keeps us from locking onto // an incorrect method just because it had the current number of params that we've typed so far if (methodMatchInfo->mPrevBestIdx == -1) { methodMatchInfo->mHadExactMatch = true; methodMatchInfo->mBestIdx = (int) methodMatchInfo->mInstanceList.size() - 1; } } hadMatch = true; mBestMethodDef = checkMethod; NoMatch: if (!hadMatch) { if (mBestMethodDef != NULL) return true; //TODO: /*if ((mHadExplicitGenericArguments) && (mBestMethodGenericArguments.size() != checkMethod->mGenericParams.size())) return true;*/ // At least prefer a backup method that we have an address for if ((mBackupMethodDef != NULL) && (mBackupMethodDef->mBlock.mLowPC != 0)) return true; mBackupMethodDef = checkMethod; // Lie temporarily to store at least one candidate (but mBestMethodDef is still NULL) hadMatch = true; } if (hadMatch) { mBestMethodTypeInstance = typeInstance; if (!mHadExplicitGenericArguments) { mBestMethodGenericArguments = mCheckMethodGenericArguments; } } Done: if ((autoComplete != NULL) && (autoComplete->mIsCapturingMethodMatchInfo) && (genericArgumentsSubstitute != NULL)) { auto methodMatchInfo = autoComplete->mMethodMatchInfo; methodMatchInfo->mInstanceList[methodMatchInfo->mInstanceList.size() - 1].mDwGenericArguments = *genericArgumentsSubstitute; } return true; } bool DwMethodMatcher::CheckType(DbgType* typeInstance, bool isFailurePass) { typeInstance = typeInstance->RemoveModifiers(); if (typeInstance->IsPointer()) typeInstance = typeInstance->mTypeParam; typeInstance = typeInstance->GetPrimaryType(); //bool allowPrivate = typeInstance == mExprEvaluator->GetCurrentType(); //bool allowProtected = allowPrivate || mExprEvaluator->TypeIsSubTypeOf(mExprEvaluator->GetCurrentType(), typeInstance); bool allowPrivate = true; bool allowProtected = true; auto curType = typeInstance; bool wantCtor = false; auto baseItr = typeInstance->mBaseTypes.begin(); auto altItr = typeInstance->mAlternates.begin(); while (true) { bool foundMethodInType = false; curType->PopulateType(); // Why did we have this "&&" check on there? // It made it so invocations would fail unless we had autocompleted the type if ((curType->mNeedsGlobalsPopulated) /*&& ((curType->IsNamespace()) || (curType->IsRoot()))*/) { // These aren't proper TPI types so we don't have any method declarations until we PopulateTypeGlobals mExprEvaluator->mDbgModule->PopulateTypeGlobals(curType); } for (auto methodNameEntry : curType->mMethodNameList) { if ((methodNameEntry->mCompileUnitId != -1) && (methodNameEntry->mName == mMethodName)) { // If we hot-replaced this type then we replaced and parsed all the methods too if (!curType->mCompileUnit->mDbgModule->IsObjectFile()) curType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId); methodNameEntry->mCompileUnitId = -1; } } auto checkMethod = curType->mMethodList.mHead; while (checkMethod != NULL) { /*if ((!checkMethod->mHasThis) && (!checkStatic)) continue; if ((checkMethod->mHasThis) && (!checkNonStatic)) continue;*/ // These can only be invoked when the target itself is the interface /*if (checkMethod->mExplicitInterface != NULL) continue;*/ if ((wantCtor) && (checkMethod->mMethodType != DbgMethodType_Ctor)) { checkMethod = checkMethod->mNext; continue; } if (!wantCtor) { if (checkMethod->mMethodType != DbgMethodType_Normal) { checkMethod = checkMethod->mNext; continue; } if ((checkMethod->mName == NULL) || (checkMethod->mName != mMethodName)) { checkMethod = checkMethod->mNext; continue; } } if ((!isFailurePass) && (!CheckProtection(checkMethod->mProtection, allowProtected, allowPrivate))) { checkMethod = checkMethod->mNext; continue; } if (!curType->mHasGlobalsPopulated) mExprEvaluator->mDbgModule->PopulateTypeGlobals(curType); /*if (!foundMethodInType) { if (curType->mCompileUnit->mLanguage != DbgLanguage_Beef) { String fullMethodName = String(curType->mTypeName) + "::" + mMethodName; curType->mCompileUnit->mDbgModule->EnsureMethodMapped(fullMethodName.c_str()); } foundMethodInType = true; }*/ if (!CheckMethod(curType, checkMethod)) return false; checkMethod = checkMethod->mNext; } if (mBestMethodDef != NULL) { if ((mBestMethodDef->mVirtual) && (mBestMethodDef->mVTableLoc == -1)) { // It's an override, keep searching through the base types to find the original method declaration mBestMethodDef = NULL; mBackupMethodDef = NULL; } else return true; } /*if (baseItr != typeInstance->mBaseTypes.end()) { auto baseTypeEntry = *baseItr; curType = baseTypeEntry->mBaseType; baseItr++; continue;; }*/ if (altItr != typeInstance->mAlternates.end()) { curType = *altItr; ++altItr; continue; } /*allowPrivate = false; if ((isFailurePass) && (mBackupMethodDef != NULL)) break;*/ break; } if (mBestMethodDef == NULL) { // FAILED, but select the first method which will fire an actual error on param type matching mBestMethodDef = mBackupMethodDef; for (auto subType : typeInstance->mSubTypeList) { if ((subType->mName == NULL) || (subType->IsGlobalsContainer())) { if (!CheckType(subType, isFailurePass)) return false; if (mBestMethodDef != NULL) break; } } if (mBestMethodDef == NULL) { for (auto baseTypeEntry : typeInstance->mBaseTypes) { if (!CheckType(baseTypeEntry->mBaseType, isFailurePass)) return false; if (mBestMethodDef != NULL) break; } } } return true; } ////////////////////////////////////////////////////////////////////////// DbgExprEvaluator::DbgExprEvaluator(WinDebugger* winDebugger, DbgModule* dbgModule, BfPassInstance* passInstance, int callStackIdx, int cursorPos) { mCountResultOverride = -1; mCapturingChildRef = true; mPassInstance = passInstance; mDebugger = winDebugger; mLanguage = DbgLanguage_NotSet; mOrigDbgModule = dbgModule; if (dbgModule != NULL) { mDebugTarget = dbgModule->mDebugTarget; mDbgModule = dbgModule->GetLinkedModule(); } else { mDebugTarget = NULL; mDbgModule = NULL; } mDbgCompileUnit = NULL; mExplicitThisExpr = NULL; mExpectingType = NULL; mCurMethod = NULL; mIgnoreErrors = false; mCallStackIdx = callStackIdx; mCursorPos = cursorPos; mAutoComplete = NULL; mIsEmptyTarget = (dbgModule == NULL) || (dbgModule->mDebugTarget->mIsEmpty); mExpressionFlags = DwEvalExpressionFlag_None; mHadSideEffects = false; mBlockedSideEffects = false; mReferenceId = NULL; mIsComplexExpression = false; mHadMemberReference = false; mCreatedPendingCall = false; mValidateOnly = false; mReceivingValue = NULL; mCallResults = NULL; mCallResultIdx = 0; mPropGet = NULL; mPropSet = NULL; mPropSrc = NULL; } DbgExprEvaluator::~DbgExprEvaluator() { } DbgTypedValue DbgExprEvaluator::GetInt(int value) { DbgTypedValue dbgValue; dbgValue.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); dbgValue.mInt32 = value; return dbgValue; } DbgTypedValue DbgExprEvaluator::GetString(const StringImpl& str) { String* resultPtr; mDebugger->mLiteralSet.TryAdd(str, &resultPtr); DbgTypedValue dbgValue; auto language = GetLanguage(); auto charPtrType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar : DbgType_SChar, language); if (charPtrType != NULL) charPtrType = mDbgModule->GetPointerType(charPtrType); if (charPtrType != NULL) { dbgValue.mType = charPtrType; dbgValue.mLocalPtr = resultPtr->c_str(); dbgValue.mIsLiteral = true; dbgValue.mDataLen = resultPtr->mLength; } return dbgValue; } void DbgExprEvaluator::Fail(const StringImpl& error, BfAstNode* node) { if ((!mIgnoreErrors) && (mPassInstance != NULL)) mPassInstance->Fail(error, node); } void DbgExprEvaluator::Warn(const StringImpl& error, BfAstNode * node) { if ((!mIgnoreErrors) && (mPassInstance != NULL)) mPassInstance->Warn(0, error, node); } DbgType* DbgExprEvaluator::GetExpectingType() { if ((mExpectingType == NULL) && (!mExpectingTypeName.empty())) { mExpectingType = ResolveTypeRef(mExpectingTypeName); mExpectingTypeName.clear(); } return mExpectingType; } void DbgExprEvaluator::GetNamespaceSearch() { if (!mNamespaceSearch.empty()) return; auto currentMethod = GetCurrentMethod(); if (currentMethod != NULL) { auto parent = currentMethod->GetParent(); while (parent != NULL) { parent = parent->GetPrimaryType(); mNamespaceSearch.push_back(parent); parent = parent->mParent; } } if (!mNamespaceSearchStr.empty()) { auto language = GetLanguage(); int lastComma = -1; for (int i = 0; i < (int)mNamespaceSearchStr.length(); i++) { if (mNamespaceSearchStr[i] == ',') { String namespaceEntry = mNamespaceSearchStr.Substring(lastComma + 1, i - lastComma - 1); auto dbgTypeEntry = mDbgModule->mTypeMap.Find(namespaceEntry.c_str(), language); if (dbgTypeEntry != NULL) mNamespaceSearch.push_back(dbgTypeEntry->mValue); lastComma = i; } } String namespaceEntry = mNamespaceSearchStr.Substring(lastComma + 1); auto dbgTypeEntry = mDbgModule->mTypeMap.Find(namespaceEntry.c_str(), language); if (dbgTypeEntry != NULL) mNamespaceSearch.push_back(dbgTypeEntry->mValue); } } DbgType* DbgExprEvaluator::ResolveSubTypeRef(DbgType* checkType, const StringImpl& name) { checkType->PopulateType(); for (auto subType : checkType->mSubTypeList) { if (strcmp(subType->mTypeName, name.c_str()) == 0) return subType; } for (auto checkBaseType : checkType->mBaseTypes) { DbgType* subType = ResolveSubTypeRef(checkBaseType->mBaseType, name); if (subType != NULL) return subType; } return NULL; } DbgType* DbgExprEvaluator::FixType(DbgType* dbgType) { if (dbgType->IsBfObject()) dbgType = mDbgModule->GetPointerType(dbgType); return dbgType; } DbgTypedValue DbgExprEvaluator::FixThis(const DbgTypedValue& thisVal) { /*if ((thisVal.mType != NULL) && (thisVal.mType->GetLanguage() == DbgLanguage_Beef) && (!thisVal.mType->IsBfObjectPtr()) && (thisVal.mType->IsPointer())) { // Beef structs expect 'this' as a valuetype, not a pointer DbgTypedValue fixedThisVal; fixedThisVal.mType = thisVal.mType->mTypeParam; fixedThisVal.mSrcAddress = thisVal.mPtr; fixedThisVal.mPtr = thisVal.mPtr; int byteCount = fixedThisVal.mType->GetByteCount(); if (byteCount <= 8) // For typed primitive loads mDebugger->ReadMemory(fixedThisVal.mSrcAddress, byteCount, &fixedThisVal.mInt64); return fixedThisVal; }*/ return thisVal; } DbgType* DbgExprEvaluator::ResolveTypeRef(BfTypeReference* typeRef) { mDbgModule->ParseTypeData(); if (auto ptrTypeRef = BfNodeDynCastExact(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(typeRef)) { if (arrayTypeRef->mParams.size() == 1) { if (auto literalExpr = BfNodeDynCastExact(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(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(); 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(); mDebugTarget->mTargetBinary->ParseTypeData(); auto typeTypeEntry = mDebugTarget->mTargetBinary->FindType("System.Type", DbgLanguage_Beef); if ((typeTypeEntry == NULL) || (typeTypeEntry->mValue == NULL)) return DbgTypedValue(); auto typeType = typeTypeEntry->mValue; if (typeType->mNeedsGlobalsPopulated) typeType->mCompileUnit->mDbgModule->PopulateTypeGlobals(typeType); for (auto member : typeType->mMemberList) { if ((member->mIsStatic) && (member->mName != NULL) && (strcmp(member->mName, "sTypes") == 0) && (member->mLocationData != NULL)) { auto stackFrame = GetStackFrame(); DbgAddrType addrType; intptr 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); return typedVal; } } } return DbgTypedValue(); } void DbgExprEvaluator::BeefStringToString(addr_target addr, String& outStr) { mDebugTarget->GetCompilerSettings(); int objectSize = mDebugTarget->mBfObjectSize; int32 strLen = 0; addr_target charPtr = 0; if (!mDebugTarget->mBfHasLargeStrings) { strLen = mDebugger->ReadMemory(addr + objectSize); int32 allocSizeAndFlags = mDebugger->ReadMemory(addr + objectSize + 4); if ((allocSizeAndFlags & 0x40000000) != 0) charPtr = mDebugger->ReadMemory(addr + objectSize + 4 + 4); else charPtr = addr + objectSize + 4 + 4; } else { strLen = mDebugger->ReadMemory(addr + objectSize); int64 allocSizeAndFlags = mDebugger->ReadMemory(addr + objectSize + 8); if ((allocSizeAndFlags & 0x40000000'00000000LL) != 0) charPtr = mDebugger->ReadMemory(addr + objectSize + 8 + 8); else charPtr = addr + objectSize + 4 + 4; } if ((strLen > 0) && (strLen < 4096)) { outStr.Append('?', strLen); mDebugger->ReadMemory(charPtr, strLen, &outStr[outStr.length() - strLen]); } } void DbgExprEvaluator::BeefStringToString(const DbgTypedValue& val, String& outStr) { mDebugTarget->GetCompilerSettings(); auto useVal = val; if (useVal.mType->IsBfObjectPtr()) { useVal.mType = useVal.mType->mTypeParam; useVal.mSrcAddress = useVal.mPtr; } BeefStringToString(useVal.mSrcAddress, outStr); } void DbgExprEvaluator::BeefTypeToString(const DbgTypedValue& val, String& outStr) { if (!val) return; mDebugTarget->GetCompilerSettings(); auto useVal = val; if (useVal.mType->IsBfObjectPtr()) { useVal.mType = useVal.mType->mTypeParam; useVal.mSrcAddress = useVal.mPtr; } typedef int32 _TypeId; typedef uint32 _TypeFlags; typedef int8 _TypeCode; #pragma pack(push, 1) struct _Type { int32 mSize; _TypeId mTypeId; _TypeFlags mTypeFlags; int32 mMemberDataOffset; _TypeCode mTypeCode; uint8 mAlign; }; struct _PointerType : _Type { int16 mPadding0; _TypeCode mElementType; }; struct _SizedArrayType : _Type { int16 mPadding0; _TypeId mElementType; int32 mElementCount; }; struct _String { }; struct _MethodData { }; struct _FieldData { }; struct _ClassVData { }; struct _TypeInstance : public _Type { #ifdef BF_DBG_32 int16 mPadding0; #else int16 mPadding0; int32 mPadding1; #endif addr_target mTypeClassVData; addr_target mName; addr_target mNamespace; int32 mInstSize; int32 mInstAlign; int32 mCustomAttributesIdx; _TypeId mBaseType; _TypeId mUnderlyingType; _TypeId mOuterType; int32 mInheritanceId; int32 mInheritanceCount; uint8 mInterfaceSlot; uint8 mInterfaceCount; int16 mMethodDataCount; int16 mPropertyDataCount; int16 mFieldDataCount; addr_target mInterfaceDataPtr; addr_target mMethodDataPtr; addr_target mPropertyDataPtr; addr_target mFieldDataPtr; addr_target mCustomAttrDataPtr; }; struct _SpecializedGenericType : _TypeInstance { _TypeId mUnspecializedType; #ifdef BF_DBG_64 int32 mPadding0; #endif addr_target mResolvedTypeRefs; }; struct _ArrayType : _SpecializedGenericType { int32 mElementSize; uint8 mRank; uint8 mElemensDataOffset; }; #pragma pack(pop) int typeIdSize = sizeof(_TypeId); int ptrSize = (int)sizeof(addr_target); int objectSize = mDebugTarget->mBfObjectSize; int typeSize = sizeof(_Type); int typeInstanceSize = objectSize + sizeof(_TypeInstance); auto addr = useVal.mSrcAddress; auto typeAddr = addr + objectSize; _TypeFlags typeFlags = mDebugger->ReadMemory<_TypeFlags>(typeAddr + offsetof(_Type, mTypeFlags)); if ((typeFlags & BfTypeFlags_Array) != 0) { _TypeId unspecializedTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_SpecializedGenericType, mUnspecializedType)); addr_target elementsArrayAddr = mDebugger->ReadMemory(typeAddr + offsetof(_SpecializedGenericType, mResolvedTypeRefs)); _TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(elementsArrayAddr); auto elementType = GetBeefTypeById(elementTypeId); BeefTypeToString(elementType, outStr); outStr += "["; int rank = mDebugger->ReadMemory(typeAddr + offsetof(_ArrayType, mRank)); for (int commaIdx = 0; commaIdx < rank - 1; commaIdx++) outStr += ","; outStr += "]"; } else if ((typeFlags & BfTypeFlags_Pointer) != 0) { _TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_PointerType, mElementType)); auto elementType = GetBeefTypeById(elementTypeId); BeefTypeToString(elementType, outStr); outStr += "*"; } else if ((typeFlags & BfTypeFlags_Delegate) != 0) { outStr += "delegate"; } else if ((typeFlags & BfTypeFlags_Function) != 0) { outStr += "function"; } // else if ((typeFlags & BfTypeFlags_Tuple) != 0) // { // outStr += "function"; // } else if ((typeFlags & BfTypeFlags_SizedArray) != 0) { _TypeId elementTypeId = mDebugger->ReadMemory<_TypeId>(typeAddr + objectSize + offsetof(_SizedArrayType, mElementType)); auto elementType = GetBeefTypeById(elementTypeId); BeefTypeToString(elementType, outStr); int elementCount = mDebugger->ReadMemory(typeAddr + objectSize + offsetof(_SizedArrayType, mElementCount)); outStr += StrFormat("[%d]", elementCount); } else if (((typeFlags & BfTypeFlags_Struct) != 0) || ((typeFlags & BfTypeFlags_TypedPrimitive) != 0) || ((typeFlags & BfTypeFlags_Object) != 0)) { addr_target addr0 = typeAddr; addr_target addr1 = typeAddr + offsetof(_Type, mTypeFlags); addr_target addr2 = typeAddr + offsetof(_Type, mAlign); addr_target addr3 = typeAddr + offsetof(_TypeInstance, mPadding0); addr_target addr4 = typeAddr + offsetof(_TypeInstance, mTypeClassVData); addr_target namePtr = mDebugger->ReadMemory(typeAddr + offsetof(_TypeInstance, mName)); addr_target namespacePtr = mDebugger->ReadMemory(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(unspecializedType.mSrcAddress + typeInstanceSize); if (outerType) { int outerUnspecializedTypeId = mDebugger->ReadMemory<_TypeId>(outerType.mSrcAddress + typeInstanceSize); auto outerUnspecializedType = GetBeefTypeById(outerUnspecializedTypeId); startGenericIdx = mDebugger->ReadMemory(outerUnspecializedType.mSrcAddress + typeInstanceSize); } addr_target genericParamsPtr = mDebugger->ReadMemory(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(typeAddr + (int)offsetof(_Type, mTypeCode)); if (typeCode == BfTypeCode_Pointer) { _TypeId elementType = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_PointerType, mElementType)); BeefTypeToString(GetBeefTypeById(elementType), outStr); outStr += "*"; return; } switch (typeCode) { case BfTypeCode_None: outStr += "void"; break; case BfTypeCode_CharPtr: outStr += "char8*"; break; case BfTypeCode_Pointer: outStr += "*"; break; case BfTypeCode_NullPtr: outStr += ""; break; case BfTypeCode_Var: outStr += "var"; break; case BfTypeCode_Let: outStr += "let"; break; case BfTypeCode_Boolean: outStr += "bool"; break; case BfTypeCode_Int8: outStr += "int8"; break; case BfTypeCode_UInt8: outStr += "uint8"; break; case BfTypeCode_Int16: outStr += "int16"; break; case BfTypeCode_UInt16: outStr += "uint16"; break; case BfTypeCode_Int32: outStr += "int32"; break; case BfTypeCode_UInt32: outStr += "uint32"; break; case BfTypeCode_Int64: outStr += "int64"; break; case BfTypeCode_UInt64: outStr += "uint64"; break; case BfTypeCode_IntPtr: outStr += "int"; break; case BfTypeCode_UIntPtr: outStr += "uint"; break; case BfTypeCode_IntUnknown: outStr += "int unknown"; break; case BfTypeCode_UIntUnknown: outStr += "uint unknown"; break; case BfTypeCode_Char8: outStr += "char8"; break; case BfTypeCode_Char16: outStr += "char16"; break; case BfTypeCode_Char32: outStr += "char32"; break; case BfTypeCode_Float: outStr += "float"; break; case BfTypeCode_Double: outStr += "double"; break; } } } CPUStackFrame* DbgExprEvaluator::GetStackFrame() { if (mIsEmptyTarget) return NULL; static CPUStackFrame emptyStackFrame; if (mCallStackIdx == -1) return &emptyStackFrame; if (mDebugger->mRunState == RunState_NotStarted) return &emptyStackFrame; if (mDebugger->mCallStack.size() == 0) mDebugger->UpdateCallStack(); if (mCallStackIdx >= (int)mDebugger->mCallStack.size()) return &emptyStackFrame; return mDebugger->mCallStack[mCallStackIdx]; } CPURegisters* DbgExprEvaluator::GetRegisters() { if (mIsEmptyTarget) return NULL; auto stackFrame = GetStackFrame(); if (stackFrame != NULL) return &stackFrame->mRegisters; return NULL; } DbgTypedValue DbgExprEvaluator::GetRegister(const StringImpl& regName) { if (mIsEmptyTarget) return DbgTypedValue(); if (!mDebugger->mIsRunning) return DbgTypedValue(); Array* regForms = NULL; if (mCallStackIdx != -1) { if (mCallStackIdx < (int)mDebugger->mCallStack.size()) regForms = &mDebugger->mCallStack[mCallStackIdx]->mRegForms; } DbgTypedValue result = mDebugger->GetRegister(regName, GetLanguage(), GetRegisters(), regForms); if ((result) && (mReferenceId != NULL)) { int regNum = CPURegisters::GetCompositeRegister(result.mRegNum); const char* regName = CPURegisters::GetRegisterName(regNum); if (regName != NULL) *mReferenceId = String("$") + regName; } return result; } DbgSubprogram* DbgExprEvaluator::GetCurrentMethod() { if (mIsEmptyTarget) return NULL; if (!mDebugger->mIsRunning) return NULL; if (mCallStackIdx == -1) return NULL; mDebugger->UpdateCallStackMethod(mCallStackIdx); if (mCallStackIdx >= (int)mDebugger->mCallStack.size()) return NULL; auto callStack = mDebugger->mCallStack[mCallStackIdx]; auto subProgram = callStack->mSubProgram; if (subProgram != NULL) subProgram->PopulateSubprogram(); return subProgram; } DbgType* DbgExprEvaluator::GetCurrentType() { auto curMethod = GetCurrentMethod(); if (curMethod != NULL) return curMethod->mParentType; return NULL; } DbgTypedValue DbgExprEvaluator::GetThis() { if (mExplicitThis) return mExplicitThis; String findName = "this"; CPUStackFrame* stackFrame = GetStackFrame(); if (stackFrame != NULL) { intptr valAddr; DbgType* valType; DbgAddrType addrType = DbgAddrType_Value; if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType)) { //valType = valType->RemoveModifiers(); return FixThis(ReadTypedValue(NULL, valType, valAddr, addrType)); } if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType)) { DbgTypedValue result = ReadTypedValue(NULL, valType, valAddr, addrType); if (!result) return result; SetAndRestoreValue prevIgnoreError(mIgnoreErrors, true); return FixThis(LookupField(NULL, result, "__this")); } } return DbgTypedValue(); } DbgTypedValue DbgExprEvaluator::GetDefaultTypedValue(DbgType* srcType) { DbgTypedValue typedValue; typedValue.mType = srcType; return typedValue; } String DbgExprEvaluator::TypeToString(DbgType* type) { return type->ToString(); } bool DbgExprEvaluator::CheckHasValue(DbgTypedValue typedValue, BfAstNode* refNode) { if ((!typedValue) && (typedValue.mType != NULL)) { mResult = DbgTypedValue(); Fail("Value required", refNode); return false; } return true; } //TODO: Expand this bool DbgExprEvaluator::CanCast(DbgTypedValue typedVal, DbgType* toType, BfCastFlags castFlags) { DbgType* fromType = typedVal.mType; if (fromType == toType) return true; if ((fromType->IsConst()) && (!toType->IsConst())) return false; fromType = fromType->RemoveModifiers(); toType = toType->RemoveModifiers(); // Ptr -> const Ptr if ((fromType->IsPointer()) && (toType->IsPointer())) { if (toType->mTypeParam->IsConst()) { auto primaryFrom = fromType->mTypeParam->GetPrimaryType(); auto primaryTo = toType->mTypeParam->mTypeParam->GetPrimaryType(); if ((primaryFrom->IsCompositeType()) || (primaryTo->IsCompositeType())) { if (primaryFrom == primaryTo) return true; } else if (primaryFrom->IsPrimitiveType()) { return primaryFrom->mTypeCode == primaryTo->mTypeCode; } } } if ((fromType->IsCompositeType()) && ((toType->IsCompositeType() || (toType->IsInterface())))) { bool allowCast = false; //TODO: Allow explicit interface casts on any object //TODO: Insert cast checking code int thisOffset = 0; addr_target thisVal = typedVal.mSrcAddress; if (TypeIsSubTypeOf(fromType, toType, &thisOffset, &thisVal)) { return true; } } if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType())) { DbgTypeCode fromTypeCode = fromType->mTypeCode; DbgTypeCode toTypeCode = toType->mTypeCode; if ((fromTypeCode == toTypeCode)) return true; // Must be from a default int to do an implicit constant cast, not casted by user, ie: (ushort)123 if ((toType->IsInteger()) /*&& (typedVal.mValue != NULL) && (fromTypeCode == DbgType_i32)*/) { // Allow constant ints to be implicitly downcasted if they fit if (typedVal.mIsLiteral) { int64 srcVal = typedVal.GetInt64(); if (toType->IsSigned()) { int64 minVal = -(1LL << (8 * toType->mSize - 1)); int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) return true; } else if (toType->mSize == 8) // ulong { if (srcVal > 0) return true; } else { int64 minVal = 0; int64 maxVal = (1LL << (8 * toType->mSize)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) return true; } } } switch (toTypeCode) { case DbgType_u8: case DbgType_UChar: switch (fromTypeCode) { case DbgType_UChar: case DbgType_u8: return true; } break; case DbgType_SChar16: case DbgType_i16: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_SChar: case DbgType_SChar16: return true; case DbgType_u8: case DbgType_UChar: return true; } break; case DbgType_u16: switch (fromTypeCode) { case DbgType_i8: return true; case DbgType_u8: case DbgType_UChar: case DbgType_UChar16: return true; } break; case DbgType_SChar32: case DbgType_i32: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: return true; case DbgType_u8: case DbgType_u16: case DbgType_UChar: case DbgType_UChar16: return true; } break; case DbgType_UChar32: case DbgType_u32: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_SChar: case DbgType_SChar16: return true; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: return true; } break; case DbgType_i64: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_i64: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return true; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: return true; } break; case DbgType_u64: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return true; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar32: return true; } break; case DbgType_Single: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: return true; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar32: return true; } break; case DbgType_Double: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_i64: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return true; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: return true; case DbgType_Single: return true; } break; } } return false; } DbgTypedValue DbgExprEvaluator::Cast(BfAstNode* srcNode, const DbgTypedValue& typedVal, DbgType* toType, bool explicitCast, bool silentFail) { if (!typedVal) return typedVal; DbgType* fromType = typedVal.mType; fromType = fromType->RemoveModifiers(); toType = toType->RemoveModifiers(); fromType = fromType->GetPrimaryType(); toType = toType->GetPrimaryType(); if ((toType->IsBfObject()) && (fromType->IsPointer())) toType = toType->GetDbgModule()->GetPointerType(toType); if (toType->IsPrimitiveType()) { if (fromType->mTypeCode == toType->mTypeCode) return typedVal; if ((fromType->IsStruct()) || (fromType->IsEnum())) { if (fromType->mTypeParam != NULL) { DbgTypedValue underlyingTypedValue = typedVal; underlyingTypedValue.mType = fromType->mTypeParam; auto castedVal = Cast(srcNode, underlyingTypedValue, toType, explicitCast, true); if (castedVal) return castedVal; } } } if (fromType == toType) return typedVal; if ((fromType->mTypeCode == DbgType_Null) && (toType->mTypeCode == DbgType_Ptr)) { DbgTypedValue val; val.mPtr = typedVal.mPtr; val.mType = toType; return val; } auto curTypeInstance = GetCurrentType(); if ((fromType->IsCompositeType()) && ((toType->IsCompositeType() || (toType->IsInterface())))) { bool allowCast = false; //TODO: Allow explicit interface casts on any object //TODO: Insert cast checking code int thisOffset = 0; addr_target thisVal = typedVal.mSrcAddress; if (TypeIsSubTypeOf(fromType, toType, &thisOffset, &thisVal)) { allowCast = true; } else if ((explicitCast) && ((toType->IsInterface()) || (TypeIsSubTypeOf(toType, fromType, &thisOffset)))) { thisOffset = -thisOffset; allowCast = true; } if (fromType->IsBfPayloadEnum()) { if (!allowCast) { for (auto member : fromType->mMemberList) { if (member->mType->Equals(toType)) { allowCast = true; break; } } } } if (allowCast) { DbgTypedValue val; //val.mPtr = typedVal.mPtr + thisOffset; val.mInt64 = typedVal.mInt64; val.mSrcAddress = thisVal + thisOffset; val.mType = toType; // Doing this allows us to modify a typed value that is tied to a register through its '[base]' if ((typedVal.mRegNum != -1) && (val.mType->GetByteCount() == typedVal.mType->GetByteCount())) val.mRegNum = typedVal.mRegNum; return val; } } // Payload discriminator if ((fromType->IsBfPayloadEnum()) && (toType->IsInteger())) { intptr valAddr; DbgType* valType; DbgAddrType addrType = DbgAddrType_Value; String findName = "$"; //BF_ASSERT(typedVal.mVariable != NULL); if (typedVal.mVariable != NULL) findName += typedVal.mVariable->mName; findName += "$d"; if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, GetStackFrame(), &valAddr, &valType, &addrType)) { return ReadTypedValue(srcNode, valType, valAddr, addrType); } } // Optimized composite - probably stored in register if ((toType->IsCompositeType()) && (fromType->IsInteger()) && (explicitCast)) { if (toType->GetByteCount() <= 8) { DbgTypedValue val; val.mType = toType; val.mInt64 = typedVal.mInt64; val.mSrcAddress = typedVal.mSrcAddress; return val; } } // Ptr -> const Ptr if ((fromType->IsPointer()) && (toType->IsPointer())) { if (toType->mTypeParam->IsConst()) { bool canCast = false; auto primaryFrom = fromType->mTypeParam->GetPrimaryType(); auto primaryTo = toType->mTypeParam->mTypeParam->GetPrimaryType(); if ((primaryFrom->IsCompositeType()) || (primaryTo->IsCompositeType())) { if (primaryFrom == primaryTo) canCast = true; } else if (primaryFrom->IsPrimitiveType()) { canCast = primaryFrom->mTypeCode == primaryTo->mTypeCode; } if (canCast) { DbgTypedValue val = typedVal; val.mType = toType; return val; } } } if ((fromType->IsPointer()) && (fromType->mTypeParam->IsCompositeType()) && (toType->IsPointer()) && (toType->mTypeParam->IsCompositeType())) { auto fromInnerType = fromType->mTypeParam->RemoveModifiers(); auto toInnerType = toType->mTypeParam->RemoveModifiers(); /*DbgTypedValue fromVal; fromVal.mType = fromInnerTytpe; fromVal.mSrcAddress = typedVal.mPtr; DbgTypedValue toVal = Cast(srcNode, fromVal, toInnerType, explicitCast, silentFail); if (toVal) { // Back to pointer form toVal.mType = toType; toVal.mPtr = toVal.mSrcAddress; toVal.mSrcAddress = 0; } return toVal;*/ bool allowCast = false; //TODO: Allow explicit interface casts on any object //TODO: Insert cast checking code int thisOffset = 0; addr_target thisVal = typedVal.mSrcAddress; if (TypeIsSubTypeOf(fromInnerType, toInnerType, &thisOffset, &thisVal)) { allowCast = true; } else if ((explicitCast) && ((toInnerType->IsInterface()) || (TypeIsSubTypeOf(toInnerType, fromInnerType, &thisOffset)))) { thisOffset = -thisOffset; allowCast = true; } if (allowCast) { DbgTypedValue toVal; toVal.mType = toType; toVal.mPtr = typedVal.mPtr + thisOffset; toVal.mSrcAddress = 0; return toVal; } } // IFace -> object|IFace if ((fromType->IsInterface()) || ((fromType->IsPointer()) && (fromType->mTypeParam->IsInterface()))) { if (toType->IsBfObject()) { DbgTypedValue val; val.mPtr = typedVal.GetPointer(); val.mType = toType; return val; } } // Int|Ptr|Arr -> Int|Ptr|Arr if (((fromType->IsInteger()) || (fromType->IsPointer()) || (fromType->IsSizedArray())) && ((toType->IsInteger()) || (toType->IsPointer()) || (toType->IsSizedArray()))) { bool allowCast = explicitCast; if (allowCast) { DbgTypedValue val; if (toType->IsSizedArray()) val.mSrcAddress = typedVal.GetPointer(); else val.mPtr = typedVal.GetPointer(); val.mType = toType; return val; } } // Enum -> Int if ((((fromType->IsBfEnum()) || (fromType->IsEnum())) && (toType->IsInteger())) && ((explicitCast) || (fromType == curTypeInstance))) { DbgTypedValue result = typedVal; result.mType = toType; return result; } // Int -> Enum if (((fromType->IsInteger()) && ((toType->IsBfEnum()) || (toType->IsEnum()))) && ((explicitCast) || (toType == curTypeInstance))) { DbgTypedValue result = typedVal; result.mType = toType; return result; } // TypedPrimitive -> Primitive if ((fromType->IsTypedPrimitive()) && (toType->IsPrimitiveType())) { DbgTypedValue fromValue = typedVal; fromValue.mType = fromType->GetBaseType(); return Cast(srcNode, fromValue, toType, explicitCast); } // TypedPrimitive -> TypedPrimitive if ((fromType->IsTypedPrimitive()) && (toType->IsTypedPrimitive())) { DbgTypedValue fromValue = typedVal; fromValue.mType = fromType->GetBaseType(); DbgTypedValue primTypedVal = Cast(srcNode, fromValue, toType->GetBaseType(), explicitCast, silentFail); if (primTypedVal) { primTypedVal.mType = toType; return primTypedVal; } } // Primitive -> TypedPrimitive if ((fromType->IsPrimitiveType()) && (toType->IsTypedPrimitive())) { DbgTypedValue primTypedVal = Cast(srcNode, typedVal, toType->GetBaseType(), true); if (primTypedVal) { primTypedVal.mType = toType; return primTypedVal; } } if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType())) { DbgTypeCode fromTypeCode = fromType->mTypeCode; DbgTypeCode toTypeCode = toType->mTypeCode; if ((toType->IsInteger()) && (fromType->IsIntegral())) { // Allow constant ints to be implicitly shrunk if they fit if (typedVal.mIsLiteral) { int64 srcVal = typedVal.GetInt64(); /*if (toType->IsSigned()) { int64 minVal = -(1LL << (8 * toType->mSize - 1)); int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) explicitCast = true; } else if (toType->mSize == 8) // ulong { if (srcVal > 0) explicitCast = true; } else { int64 minVal = 0; int64 maxVal = (1LL << (8 * toType->mSize)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) explicitCast = true; }*/ if (toType->IsChar()) { if (srcVal == 0) explicitCast = true; } else if ((fromType->IsChar()) && (!toType->IsChar())) { // Never allow this } else if ((fromTypeCode == BfTypeCode_UInt64) && (srcVal < 0)) { // There's nothing that this could fit into } else if (toType->IsSigned()) { int64 minVal = -(1LL << (8 * toType->mSize - 1)); int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) explicitCast = true; } else if (toType->mSize == 8) // ulong { if (srcVal >= 0) explicitCast = true; } else { int64 minVal = 0; int64 maxVal = (1LL << (8 * toType->mSize)) - 1; if ((srcVal >= minVal) && (srcVal <= maxVal)) explicitCast = true; } } } DbgTypedValue result; result.mType = toType; if (((fromType->IsInteger()) || (fromType->IsChar())) && (toType->IsInteger()) || (toType->IsChar())) { result.mInt64 = typedVal.GetInt64(); if (explicitCast) return result; } switch (toTypeCode) { case DbgType_u8: case DbgType_UChar: switch (fromTypeCode) { case DbgType_u8: case DbgType_UChar: return result; } break; case DbgType_i16: case DbgType_SChar16: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_SChar: case DbgType_SChar16: return result; case DbgType_u8: case DbgType_UChar: return result; } break; case DbgType_u16: case DbgType_UChar16: switch (fromTypeCode) { case DbgType_i8: case DbgType_SChar: return result; case DbgType_u8: case DbgType_UChar: return result; } break; case DbgType_i32: case DbgType_SChar32: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return result; case DbgType_u8: case DbgType_u16: case DbgType_UChar: case DbgType_UChar16: return result; } break; case DbgType_UChar32: case DbgType_u32: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_SChar: case DbgType_SChar16: return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: return result; } break; case DbgType_i64: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: return result; } break; case DbgType_u64: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar32: return result; } break; case DbgType_Single: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_i64: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: result.mSingle = typedVal.GetInt64(); return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_u64: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: result.mSingle = typedVal.GetInt64(); return result; } break; case DbgType_Double: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_i64: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: result.mDouble = typedVal.GetInt64(); return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_u64: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: result.mDouble = typedVal.GetInt64(); return result; case DbgType_Single: result.mDouble = typedVal.mSingle; return result; } break; } if (explicitCast) { if (toType->IsInteger()) { if (typedVal.mType->IsInteger()) { return result; } else if (typedVal.mType->IsFloat()) { if (typedVal.mType->mTypeCode == DbgType_Single) result.mInt64 = typedVal.mSingle; else result.mInt64 = (int64)typedVal.mDouble; return result; } } switch (toTypeCode) { case DbgType_Single: switch (fromTypeCode) { case DbgType_i8: case DbgType_i16: case DbgType_i32: case DbgType_SChar: case DbgType_SChar16: case DbgType_SChar32: result.mSingle = (float)typedVal.GetInt64(); return result; case DbgType_u8: case DbgType_u16: case DbgType_u32: case DbgType_UChar: case DbgType_UChar16: case DbgType_UChar32: result.mSingle = (float)typedVal.GetInt64(); return result; case DbgType_Double: result.mSingle = (float)typedVal.mDouble; return result; } break; } } } if (fromType->mTypeCode == DbgType_TypeDef) { DbgTypedValue realTypedVal = typedVal; realTypedVal.mType = fromType->mTypeParam; return Cast(srcNode, realTypedVal, toType, explicitCast, silentFail); } if (toType->mTypeCode == DbgType_TypeDef) return Cast(srcNode, typedVal, toType->mTypeParam, explicitCast, silentFail); if (!silentFail) { auto language = GetLanguage(); const char* errStr = explicitCast ? "Unable to cast '%s' to '%s'" : "Unable to implicitly cast '%s' to '%s'"; Fail(StrFormat(errStr, fromType->ToString(language).c_str(), toType->ToString(language).c_str()), srcNode); } return DbgTypedValue(); } bool DbgExprEvaluator::HasField(DbgType* curCheckType, const StringImpl& fieldName) { curCheckType = curCheckType->RemoveModifiers(); curCheckType = curCheckType->GetPrimaryType(); curCheckType->PopulateType(); for (auto checkMember : curCheckType->mMemberList) { if (checkMember->mName == NULL) { auto checkResult = HasField(checkMember->mType, fieldName); if (checkResult) return checkResult; } else if (checkMember->mName == fieldName) return true; } for (auto baseTypeEntry : curCheckType->mBaseTypes) { auto baseType = baseTypeEntry->mBaseType; auto result = HasField(baseType, fieldName); if (result) return result; } //if (wantsStatic) { // Look for statics in anonymous inner classes for (auto checkSubType : curCheckType->mSubTypeList) { if (checkSubType->mTypeName == NULL) { auto result = HasField(checkSubType, fieldName); if (result) return result; } } for (auto altType : curCheckType->mAlternates) { auto result = HasField(altType, fieldName); if (result) return result; } } return false; } DbgTypedValue DbgExprEvaluator::DoLookupField(BfAstNode* targetSrc, DbgTypedValue target, DbgType* curCheckType, const StringImpl& fieldName, CPUStackFrame* stackFrame, bool allowImplicitThis) { bool wantsStatic = (!target) || (target.mHasNoValue); auto flavor = GetFlavor(); auto language = GetLanguage(); if (language != DbgLanguage_Beef) { // C allows static lookups by non-static member reference wantsStatic = true; } curCheckType = curCheckType->RemoveModifiers(); curCheckType = curCheckType->GetPrimaryType(); curCheckType->PopulateType(); //BfLogDbgExpr("DoLookupField %s %s Priority=%d\n", fieldName.c_str(), curCheckType->mName, curCheckType->mPriority); if ((curCheckType->mNeedsGlobalsPopulated) && ((curCheckType->IsNamespace()) || (curCheckType->IsRoot()))) { // Global variables don't show up in any type declaration like static fields do, so we need to populate here mDbgModule->PopulateTypeGlobals(curCheckType); } /*auto checkMember = curCheckType->mMemberList.mHead; while (checkMember != NULL)*/ String findFieldName = fieldName; if ((language == DbgLanguage_Beef) && (flavor == DbgFlavor_MS) && (target.mHasNoValue)) { //if ((curCheckType->IsRoot()) || (curCheckType->IsNamespace())) //findFieldName.insert(0, "bf__"); } //for (auto checkMember : curCheckType->mMemberList) auto nextMember = curCheckType->mMemberList.mHead; while (nextMember != NULL) { auto checkMember = nextMember; nextMember = checkMember->mNext; if (checkMember->mName == NULL) { //TODO: Check inside anonymous type DbgTypedValue innerTarget; addr_target targetPtr = target.mSrcAddress; if ((target.mType != NULL) && (target.mType->HasPointer())) targetPtr = target.mPtr; innerTarget.mSrcAddress = targetPtr + checkMember->mMemberOffset; /*if (target.mPtr != 0) innerTarget.mPtr = target.mPtr + checkMember->mMemberOffset;*/ innerTarget.mType = checkMember->mType; auto checkResult = LookupField(targetSrc, innerTarget, fieldName); if (checkResult) return checkResult; } else if (checkMember->mName == findFieldName) { //BfLogDbgExpr(" Got Match\n"); //TODO: /*if (field->mIsConst) { if (fieldInstance->mStaticValue == NULL) mModule->ResolveConstField(curCheckType, field); return DbgTypedValue(fieldInstance->mStaticValue, fieldInstance->mType); }*/ //checkMember->mMemberOffset if (mReferenceId != NULL) { if (curCheckType->IsRoot()) *mReferenceId = fieldName; else if (curCheckType->IsEnum()) *mReferenceId = curCheckType->ToString(); else *mReferenceId = curCheckType->ToString() + "." + fieldName; } if ((checkMember->mLocationLen == 0) && (checkMember->mIsStatic) && (!checkMember->mIsConst)) { if (checkMember->mCompileUnit->mDbgModule->mDbgFlavor == DbgFlavor_MS) { if (curCheckType->mNeedsGlobalsPopulated) { mDbgModule->PopulateTypeGlobals(curCheckType); return DoLookupField(targetSrc, target, curCheckType, fieldName, stackFrame, allowImplicitThis); } /*String memberName = curCheckType->ToString() + "::" + checkMember->mName; mDbgModule->ParseSymbolData(); auto entry = mDbgModule->mSymbolNameMap.Find(memberName.c_str()); if (entry != NULL) { auto sym = entry->mValue; int locLen = 1 + sizeof(addr_target); uint8* locData = checkMember->mCompileUnit->mDbgModule->mAlloc.AllocBytes(locLen); locData[0] = DW_OP_addr_noRemap; *((addr_target*)(locData + 1)) = sym->mAddress; checkMember->mLocationData = locData; checkMember->mLocationLen = locLen; } else { BF_ASSERT("Static field not found" == 0); }*/ } else { continue; //BF_FATAL("Unhandled"); //TODO: Is this needed anymore since we have 'primary types'? /*auto altType = mDebugTarget->FindType(target.mType->ToString(true)); if (altType != NULL) { auto altChildNode = altType->mAlternates.mHead; while ((altType != NULL) && (checkMember->mLocationLen == 0)) { for (auto altMember : altType->mMemberList) { if (strcmp(altMember->mName, checkMember->mName)) { if (altMember->mLocationData != NULL) { checkMember->mLinkName = altMember->mLinkName; checkMember->mLocationData = altMember->mLocationData; checkMember->mLocationLen = altMember->mLocationLen; break; } } } if (altChildNode != NULL) { altType = altChildNode->mValue; altChildNode = altChildNode->mNext; } else break; } }*/ } } if (checkMember->mLocationLen != 0) { DbgAddrType addrType; intptr valAddr = checkMember->mCompileUnit->mDbgModule->EvaluateLocation(NULL, checkMember->mLocationData, checkMember->mLocationLen, stackFrame, &addrType); if ((language == DbgLanguage_Beef) && (checkMember->mType->IsConst()) && (checkMember->mType->mTypeParam->IsSizedArray())) { // We need an extra deref valAddr = mDebugger->ReadMemory(valAddr); } if (checkMember->mIsConst) addrType = DbgAddrType_Value; return ReadTypedValue(targetSrc, checkMember->mType, valAddr, addrType); } else if (checkMember->mIsConst) { return ReadTypedValue(targetSrc, checkMember->mType, (uint64)&checkMember->mConstValue, DbgAddrType_Local); } else if (checkMember->mIsStatic) { if (curCheckType->mNeedsGlobalsPopulated) { mDbgModule->PopulateTypeGlobals(curCheckType); nextMember = curCheckType->mMemberList.mHead; continue; } Fail("Static variable not found, may have be optimized out", targetSrc); //BfLogDbgExpr(" Static variable optimized out\n"); } else { if ((allowImplicitThis) && (target.mPtr == 0)) target = GetThis(); if (!target) { Fail("Cannot reference a non-static member from a static method", targetSrc); return DbgTypedValue(); } addr_target targetPtr = target.mSrcAddress; DbgTypedValue typedValue; if (targetPtr == -1) { LookupSplatMember(target, fieldName); typedValue = mResult; mResult = DbgTypedValue(); } else if ((targetPtr == 0) && (target.mPtr != 0) && (!target.mType->HasPointer())) { typedValue = ReadTypedValue(targetSrc, checkMember->mType, (uint64)&target.mPtr + checkMember->mMemberOffset, DbgAddrType_Local); } else { if (target.mType->HasPointer()) targetPtr = target.mPtr; typedValue = ReadTypedValue(targetSrc, checkMember->mType, targetPtr + checkMember->mMemberOffset, DbgAddrType_Target); } if (checkMember->mBitSize != 0) { int expectedShift = (checkMember->mType->mSize * 8) - checkMember->mBitSize; int shift = expectedShift - checkMember->mBitOffset; typedValue.mUInt64 = typedValue.mUInt64 >> shift; uint64 mask = 0; for (int i = 0; i < checkMember->mBitSize; i++) mask |= (1ULL << i); typedValue.mUInt64 &= mask; if ((checkMember->mType->IsSigned()) && ((typedValue.mUInt64 & (1LL << (checkMember->mBitSize - 1))) != 0)) { // Sign extend typedValue.mUInt64 |= ~mask; } } return typedValue; } } //checkMember = checkMember->mNext; } BF_ASSERT((mPropGet == NULL) && (mPropSet == NULL)); if (curCheckType->mMethodList.IsEmpty()) { bool hasProps = false; for (auto methodNameEntry : curCheckType->mMethodNameList) { const char* methodName = methodNameEntry->mName; if (((methodName[0] == 'g') || (methodName[0] == 's')) && ((strncmp(methodName, "get__", 5) == 0) || (strncmp(methodName, "set__", 5) == 0))) { if (strcmp(methodName + 5, fieldName.c_str()) == 0) { curCheckType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId); methodNameEntry->mCompileUnitId = -1; } } } } for (int pass = 0; pass < 2; pass++) { for (auto method : curCheckType->mMethodList) { if (method->mName != NULL) { if (((method->mName[0] == 'g') || (method->mName[0] == 's')) && ((strncmp(method->mName, "get__", 5) == 0) || (strncmp(method->mName, "set__", 5) == 0))) { if (strcmp(method->mName + 5, fieldName.c_str()) == 0) { bool isGetter = method->mName[0] == 'g'; method->PopulateSubprogram(); if (fieldName.IsEmpty()) { int expectedParams = (int)mIndexerValues.size(); if (!isGetter) expectedParams++; if (method->GetParamCount() != expectedParams) continue; } 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(target.mPtr); int32 virtThisOffset = mDebugger->ReadMemory(vtableAddr + baseTypeEntry->mVTableOffset * sizeof(int32)); *ptrRef += virtThisOffset; } else *ptrRef += baseTypeEntry->mThisOffset; } //BF_ASSERT(baseTypeEntry->mVTableOffset == -1); auto result = DoLookupField(targetSrc, baseTarget, baseType, fieldName, stackFrame, allowImplicitThis); if (result) return result; } if (wantsStatic) { // Look for statics in anonymous inner classes for (auto checkSubType : curCheckType->mSubTypeList) { bool includeSubType = ((checkSubType->mTypeName == NULL) || (checkSubType->IsGlobalsContainer())); if ((language == DbgLanguage_C) && (checkSubType->IsEnum())) includeSubType = true; if (includeSubType) { if ((checkSubType->IsGlobalsContainer()) && (checkSubType->mPriority <= DbgTypePriority_Normal)) continue; auto rawCheckSubType = checkSubType->RemoveModifiers(); auto result = DoLookupField(targetSrc, DbgTypedValue(), rawCheckSubType, fieldName, stackFrame, false); if (result) return result; } } //Turn alternates back on! /*for (auto altType : curCheckType->mAlternates) { auto result = DoLookupField(targetSrc, DbgTypedValue(), altType, fieldName, stackFrame, false); if (result) return result; }*/ } return DbgTypedValue(); } DbgTypedValue DbgExprEvaluator::LookupField(BfAstNode* targetSrc, DbgTypedValue target, const StringImpl& fieldName) { CPUStackFrame* stackFrame = GetStackFrame(); DbgType* curCheckType = NULL; /*if (currentMethod != NULL) curCheckType = currentMethod->mParentType;*/ DbgType* curMethodType = curCheckType; if (target) { if (target.mType->IsPrimitiveType()) { curCheckType = mDbgModule->GetPrimitiveStructType(target.mType->mTypeCode); if (curCheckType == NULL) return DbgTypedValue(); } else { curCheckType = target.mType; } BF_ASSERT(curCheckType != NULL); } else if (target.mType != NULL) curCheckType = target.mType; if (curCheckType != NULL) { curCheckType = curCheckType->RemoveModifiers(); if (curCheckType->mTypeCode == DbgType_Ptr) curCheckType = curCheckType->mTypeParam; } bool allowImplicitThis = false; if (curCheckType == NULL) { auto currentMethod = GetCurrentMethod(); if (currentMethod != NULL) { curCheckType = currentMethod->GetTargetType(); allowImplicitThis = currentMethod->mHasThis; } } //SetAndRestoreValue prevTypeInstance(curMethodType, curCheckType); if (curCheckType == NULL) return DbgTypedValue(); return DoLookupField(targetSrc, target, curCheckType, fieldName, stackFrame, allowImplicitThis); } void DbgExprEvaluator::Visit(BfAstNode* node) { Fail("Invalid debug expression", node); } DbgTypedValue DbgExprEvaluator::ReadTypedValue(BfAstNode* targetSrc, DbgType* dbgType, uint64 valAddr, DbgAddrType addrType) { bool failedReadMemory = false; if (addrType == DbgAddrType_Alias) { String findName = (const char*)valAddr; if (targetSrc == NULL) return DbgTypedValue(); auto source = targetSrc->GetSourceData(); auto bfParser = source->ToParser(); if (bfParser == NULL) return DbgTypedValue(); auto identifierNode = source->mAlloc.Alloc(); int srcStart = bfParser->AllocChars(findName.length()); memcpy((char*)source->mSrc + srcStart, findName.c_str(), findName.length()); identifierNode->Init(srcStart, srcStart, srcStart + findName.length()); SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, true); auto result = LookupIdentifier(identifierNode); if (result) return result; // Allow lookup in calling method if we are inlined (for mixin references) auto currentMethod = GetCurrentMethod(); if ((currentMethod != NULL) && (currentMethod->mInlineeInfo != NULL)) { SetAndRestoreValue preCallstackIdx(mCallStackIdx); mCallStackIdx++; result = LookupIdentifier(identifierNode); } return result; } if (addrType == DbgAddrType_LocalSplat) { DbgTypedValue val; val.mVariable = (DbgVariable*)valAddr; val.mType = dbgType; val.mSrcAddress = -1; val.mIsReadOnly = true; return val; } if (addrType == DbgAddrType_TargetDeref) { DbgTypedValue result = ReadTypedValue(targetSrc, dbgType, valAddr, DbgAddrType_Target); if ((result.mType != NULL) && (result.mType->IsPointer())) { result.mType = result.mType->mTypeParam; result.mSrcAddress = result.mPtr; } return result; } if (addrType == DbgAddrType_NoValue) { mPassInstance->Fail(StrFormat("No value", valAddr)); return DbgTypedValue(); } else if (addrType == DbgAddrType_OptimizedOut) { mPassInstance->Fail(StrFormat("Optimized out", valAddr)); return DbgTypedValue(); } bool hadRef = false; DbgType* origDwType = dbgType; while (true) { if (dbgType->mTypeCode == DbgType_Const) { dbgType = mDbgModule->GetInnerTypeOrVoid(dbgType); } else if ((dbgType->mTypeCode == DbgType_TypeDef) || (dbgType->mTypeCode == DbgType_Volatile)) { dbgType = mDbgModule->GetInnerTypeOrVoid(dbgType); } else if ((dbgType->mTypeCode == DbgType_Ref) || (dbgType->mTypeCode == DbgType_RValueReference)) { hadRef = true; auto innerType = dbgType->mTypeParam; if ((!innerType->IsCompositeType()) || (addrType == DbgAddrType_Target)) { if (addrType == DbgAddrType_Target) valAddr = mDebugger->ReadMemory(valAddr); if (addrType == DbgAddrType_Register) { auto registers = GetRegisters(); valAddr = registers->mIntRegsArray[valAddr]; } //valIsAddr = true; addrType = DbgAddrType_Target; //local = false; } else { //local = false; //valIsAddr = true; if (addrType == DbgAddrType_Register) { auto registers = GetRegisters(); valAddr = registers->mIntRegsArray[valAddr]; } addrType = DbgAddrType_Target; } dbgType = dbgType->mTypeParam; } else break; } dbgType = dbgType->GetPrimaryType(); if (dbgType->GetByteCount() == 0) { DbgTypedValue result; result.mType = dbgType; return result; } if (dbgType->mTypeCode == DbgType_Bitfield) { DbgType* underlyingType = dbgType->mTypeParam; DbgTypedValue result = ReadTypedValue(targetSrc, dbgType->mTypeParam, valAddr, addrType); result.mType = dbgType; auto dbgBitfieldType = (DbgBitfieldType*)dbgType; result.mUInt64 = result.mUInt64 >> dbgBitfieldType->mPosition; uint64 mask = ((uint64)1 << dbgBitfieldType->mLength) - 1; result.mUInt64 &= mask; if ((underlyingType->IsSigned()) && ((result.mUInt64 & (1LL << (dbgBitfieldType->mLength - 1))) != 0)) { // Sign extend result.mUInt64 |= ~mask; } return result; } bool local = addrType == DbgAddrType_Local; bool valIsAddr = (addrType == DbgAddrType_Local) || (addrType == DbgAddrType_Target); DbgTypedValue result; result.mType = origDwType; if (addrType == DbgAddrType_Register) { result.mRegNum = (int)valAddr; auto registers = GetRegisters(); if (result.mRegNum < CPURegisters::kNumIntRegs) result.mUInt64 = registers->mIntRegsArray[result.mRegNum]; else if ((result.mRegNum >= CPUReg_XMMREG_FIRST) && (result.mRegNum <= CPUReg_XMMREG_LAST)) { auto dwType = origDwType->RemoveModifiers(); if (dwType->IsTypedPrimitive()) dwType = dwType->GetUnderlyingType(); if (dwType->mTypeCode == DbgType_Single) result.mSingle = *(float*)((float*)registers->mXmmRegsArray + (result.mRegNum - CPUReg_XMMREG_FIRST)); else if (dwType->mTypeCode == DbgType_Double) result.mDouble = *(double*)((float*)registers->mXmmRegsArray + (result.mRegNum - CPUReg_XMMREG_FIRST)); else BF_ASSERT("Not implemented" == 0); } auto primaryType = result.mType->GetPrimaryType(); result.mType = primaryType; // In release builds, VC will place int-sized structs into a single register. This code was breaking that. /*if ((primaryType->IsCompositeType()) && (!primaryType->IsTypedPrimitive())) { result.mSrcAddress = result.mPtr; result.mPtr = 0; }*/ return result; } if (dbgType->mTypeCode == DbgType_Bitfield) dbgType = dbgType->mTypeParam; switch (dbgType->mTypeCode) { case DbgType_Void: break; case DbgType_Bool: case DbgType_i8: case DbgType_u8: case DbgType_SChar: case DbgType_UChar: if (valIsAddr) result.mInt8 = mDebugger->ReadMemory(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(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(valAddr, local, &failedReadMemory); else result.mUInt32 = (uint32)valAddr; break; case DbgType_i64: case DbgType_u64: if (valIsAddr) result.mInt64 = mDebugger->ReadMemory(valAddr, local, &failedReadMemory); else result.mUInt64 = (uint64)valAddr; break; case DbgType_Single: result.mSingle = mDebugger->ReadMemory(valAddr, local, &failedReadMemory); break; case DbgType_Double: result.mDouble = mDebugger->ReadMemory(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(valAddr, local, &failedReadMemory); else result.mPtr = valAddr; break; case DbgType_Enum: result = ReadTypedValue(targetSrc, dbgType->mTypeParam, valAddr, addrType); if (result) result.mType = dbgType; break; case DbgType_Subroutine: { auto funcPtr = result.mPtr; String symbolName; addr_target offset; DbgModule* dwarf; if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf)) { #ifdef BF_DBG_32 if ((symbolName.length() > 0) && (symbolName[0] == '_')) symbolName = symbolName.Substring(1); #endif static String demangledName; demangledName = BfDemangler::Demangle(symbolName, dbgType->GetLanguage()); DbgTypedValue result; result.mCharPtr = demangledName.c_str(); } else { result.mCharPtr = NULL; } } break; default: if (mPassInstance != NULL) mPassInstance->Fail("Invalid data type"); } if ((!local) && (valIsAddr)) { result.mSrcAddress = valAddr; } if ((failedReadMemory) && (!mValidateOnly)) { if (mPassInstance == NULL) return DbgTypedValue(); if (addrType == DbgAddrType_NoValue) mPassInstance->Fail("No value"); else if ((valAddr == 0) && (local)) mPassInstance->Fail("Optimized out"); else if (!mBlockedSideEffects) mPassInstance->Fail(StrFormat("Failed to read from address %s", EncodeDataPtr(valAddr, true).c_str())); } return result; } bool DbgExprEvaluator::CheckTupleCreation(addr_target receiveAddr, BfAstNode* targetSrc, DbgType* tupleType, const BfSizedArray& argValues, BfSizedArray* 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((*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& 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 (typeReference)) { String filter = namedTypeRef->ToString(); mAutoComplete->mInsertStartIdx = namedTypeRef->GetSrcStart(); mAutoComplete->mInsertEndIdx = namedTypeRef->GetSrcEnd(); AutocompleteAddTopLevelTypes(filter); } else if (auto qualifiedTypeRef = BfNodeDynCast(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(typeReference)) { AutocompleteCheckType(elementedTypeRef->mElementType); } } void DbgExprEvaluator::AutocompleteAddTopLevelTypes(const StringImpl& filter) { GetNamespaceSearch(); for (auto usingNamespace : mNamespaceSearch) { usingNamespace = usingNamespace->GetPrimaryType(); AutocompleteAddMembers(usingNamespace, true, false, filter); } mDbgModule->ParseGlobalsData(); int methodEntryCount = 0; //for (auto compileUnit : mDbgModule->mCompileUnits) for (int compileIdx = 0; compileIdx < (int)mDbgModule->mCompileUnits.size(); compileIdx++) { auto compileUnit = mDbgModule->mCompileUnits[compileIdx]; if (mDbgCompileUnit == NULL) continue; if ((compileUnit->mLanguage != DbgLanguage_Unknown) && (compileUnit->mLanguage != mDbgCompileUnit->mLanguage)) continue; AutocompleteAddMembers(compileUnit->mGlobalType, true, false, filter); /*for (auto dwMethod : compileUnit->mGlobalType.mMethodList) { if (dwMethod->mName != NULL) { methodEntryCount++; mAutoComplete->AddEntry(AutoCompleteEntry("method", dwMethod->mName), filter); } }*/ } auto language = GetLanguage(); if (language == DbgLanguage_Beef) { for (auto primType : mDbgModule->mBfPrimitiveTypes) { if (primType != NULL) mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primType->mName), filter); } char* primNames[2] = { "int", "uint" }; for (auto primName : primNames) mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primName), filter); } else { for (auto primType : mDbgModule->mCPrimitiveTypes) { if (primType != NULL) mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", primType->mName), filter); } } int typeEntrCount = 0; //TODO: Does this get everything? /*for (auto dwTypeEntry : mDbgModule->mTypeMap) { auto dbgType = dwTypeEntry->mValue; if ((dbgType->mParent == NULL) && (dbgType->mTypeParam == NULL) && (dbgType->mTypeCode != DbgType_Ptr) && (dbgType->mTypeCode != DbgType_Ref)) { String typeStr = dbgType->ToString(); if (typeStr[typeStr.length() - 1] == '^') continue; mAutoComplete->AddEntry(AutoCompleteEntry("type", typeStr), filter); typeEntrCount++; } }*/ } DbgTypedValue DbgExprEvaluator::LookupIdentifier(BfAstNode* identifierNode, bool ignoreInitialError, bool* hadError) { if (!mDebugger->mIsRunning) return DbgTypedValue(); auto qualifiedNameNode = BfNodeDynCast(identifierNode); if (qualifiedNameNode != NULL) { LookupQualifiedName(qualifiedNameNode, ignoreInitialError, hadError); auto qualifiedResult = mResult; mResult = DbgTypedValue(); return qualifiedResult; } String findName = identifierNode->ToString(); if ((findName.StartsWith('$')) && (findName != "$prim")) { if (IsAutoCompleteNode(identifierNode)) { String filter = identifierNode->ToString(); const char* identifiers[] = { "$ThreadId", "$ThreadName", "$TargetName", "$TargetPath", "$ModuleName", "$ModulePath", "$HitCount", "$ProcessId" }; for (int idx = 0; idx < BF_ARRAY_COUNT(identifiers); idx++) mAutoComplete->AddEntry(AutoCompleteEntry("dbg", identifiers[idx]), filter); mAutoComplete->mInsertStartIdx = identifierNode->GetSrcStart(); mAutoComplete->mInsertEndIdx = identifierNode->GetSrcEnd(); } if (findName == "$this") return GetThis(); else if (findName == "$ThreadId") return GetInt(mDebugger->mActiveThread->mThreadId); else if (findName == "$ThreadName") return GetString(mDebugger->mActiveThread->mName); else if (findName == "$TargetName") return GetString(GetFileName(mDebugTarget->mTargetPath)); else if (findName == "LaunchName") return GetString(GetFileName(mDebugTarget->mLaunchBinary->mFilePath)); else if (findName == "$TargetPath") return GetString(mDebugTarget->mTargetPath); else if (findName == "$ModuleName") return GetString(GetFileName(mDbgModule->mFilePath)); else if (findName == "$ModulePath") return GetString(mDbgModule->mFilePath); else if (findName == "$HitCount") { if (mDebugger->mActiveBreakpoint != NULL) return GetInt(mDebugger->mActiveBreakpoint->GetHeadBreakpoint()->mHitCount); return GetInt(0); } else if (findName == "$BreakpointCondition") { if ((mDebugger->mActiveBreakpoint != NULL) && (mDebugger->mActiveBreakpoint->mCondition != NULL)) return GetString(mDebugger->mActiveBreakpoint->GetHeadBreakpoint()->mCondition->mExpr); return GetString(""); } else if (findName == "$ProcessId") return GetInt(mDebugger->mProcessInfo.dwProcessId); bool mayBeRegister = true; for (int i = 1; i < findName.length(); i++) if (findName[i] == '$') mayBeRegister = false; if (mayBeRegister) { DbgTypedValue val = GetRegister(findName.Substring(1)); if (val) return val; } } DbgTypedValue result; if (mExplicitThis) { if ((mExplicitThis.mSrcAddress == -1) && ((DbgVariable*)mExplicitThis.mVariable != NULL)) { mResult = DbgTypedValue(); LookupSplatMember(mExplicitThis, findName); result = mResult; mResult = DbgTypedValue(); } else if (!result) { mExplicitThis.mType = mExplicitThis.mType->RemoveModifiers(); result = LookupField(identifierNode, mExplicitThis, findName); } if (result) { if (mExplicitThisExpr != NULL) mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(identifierNode, mCurChildRef)); return result; } } CPUStackFrame* stackFrame = GetStackFrame(); auto language = GetLanguage(); intptr valAddr; DbgType* valType; DbgAddrType addrType = DbgAddrType_None; if (IsAutoCompleteNode(identifierNode)) { String filter = identifierNode->ToString(); DbgType* dbgType = NULL; auto currentMethod = GetCurrentMethod(); auto thisVal = GetThis(); auto targetType = thisVal.mType; if (currentMethod != NULL) { if (targetType != NULL) dbgType = targetType; else if (!thisVal) dbgType = currentMethod->GetTargetType(); } Array capturedNames; Array capturedTypes; mDebugTarget->mCapturedNamesPtr = &capturedNames; mDebugTarget->mCapturedTypesPtr = &capturedTypes; { mDebugTarget->GetValueByName(GetCurrentMethod(), "*", stackFrame, &valAddr, &valType, &addrType); } mDebugTarget->mCapturedNamesPtr = NULL; mDebugTarget->mCapturedTypesPtr = NULL; auto language = GetLanguage(); BF_ASSERT(capturedTypes.size() == capturedNames.size()); { //for (auto capturedName : capturedNames) for (int i = 0; i < (int)capturedNames.size(); i++) { const String capturedName = capturedNames[i]; if (language == DbgLanguage_Beef) { // Don't put splats if ((capturedName.length() > 0) && (capturedName[0] == '$')) continue; } //TODO: Show the type icon mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(capturedTypes[i]), capturedNames[i]), filter); } } if (!mSubjectExpr.IsEmpty()) { mAutoComplete->AddEntry(AutoCompleteEntry("valuetype", "_"), filter); } if (dbgType != NULL) { dbgType = dbgType->RemoveModifiers(); if (dbgType->IsPointerOrRef()) { dbgType = dbgType->mTypeParam; dbgType = dbgType->RemoveModifiers(); } if (dbgType->mIsDeclaration) dbgType = dbgType->GetPrimaryType(); bool wantsStatic = true; bool wantsNonStatic = currentMethod->mHasThis; // In Beef, we can only access statics by class name if (dbgType->GetLanguage() != DbgLanguage_Beef) wantsStatic = !wantsNonStatic; AutocompleteAddMembers(dbgType, wantsStatic, wantsNonStatic, filter); if (currentMethod != NULL) { if (language == DbgLanguage_C) { // C++ closures auto currentMethod = GetCurrentMethod(); if (currentMethod != NULL) { if (strstr(currentMethod->mName, "operator()") != NULL) { if (mDebugTarget->GetValueByName(currentMethod, "this", stackFrame, &valAddr, &valType, &addrType)) { valType = valType->RemoveModifiers(); if (valType->IsPointer()) valType = valType->mTypeParam; valType = valType->RemoveModifiers(); AutocompleteAddMembers(valType, true, true, filter, true); } } } } else { // If in a Beef closure... if (mDebugTarget->GetValueByName(currentMethod, "__closure", stackFrame, &valAddr, &valType, &addrType)) { valType = valType->RemoveModifiers(); if (valType->IsPointer()) valType = valType->mTypeParam; AutocompleteAddMembers(valType, true, true, filter, true); } } auto targetType = currentMethod->GetTargetType(); if ((targetType != dbgType) && (targetType != NULL)) { AutocompleteAddMembers(targetType, wantsStatic, wantsNonStatic, filter); } } } mAutoComplete->mInsertStartIdx = identifierNode->GetSrcStart(); mAutoComplete->mInsertEndIdx = identifierNode->GetSrcEnd(); AutocompleteAddTopLevelTypes(filter); } if (language == DbgLanguage_C) { // For C++ lambdas, captured a "this" is named "__this", so allow lookups into that auto currentMethod = GetCurrentMethod(); if (currentMethod != NULL) { if (strstr(currentMethod->mName, "operator()") != NULL) { DbgTypedValue rootThisValue = GetThis(); if (rootThisValue) { DbgTypedValue thisValue = LookupField(identifierNode, rootThisValue, "__this"); if (thisValue) { if (findName == "this") return thisValue; auto fieldValue = LookupField(identifierNode, thisValue, findName); if (fieldValue) return fieldValue; } } } } } if (findName == "this") return GetThis(); if (findName == "_") { if (mSubjectValue) { if (mSubjectValue.mSrcAddress != 0) { auto refreshVal = ReadTypedValue(identifierNode, mSubjectValue.mType, mSubjectValue.mSrcAddress, DbgAddrType_Target); if (refreshVal) mSubjectValue = refreshVal; } return mSubjectValue; } if (!mSubjectExpr.IsEmpty()) { DwFormatInfo formatInfo; formatInfo.mLanguage = language; DbgEvaluationContext dbgEvaluationContext(mDebugger, mDbgModule, mSubjectExpr, &formatInfo); mSubjectValue = dbgEvaluationContext.EvaluateInContext(mExplicitThis); if (!mSubjectValue) { Fail("Failed to generate subject value", identifierNode); } if (mSubjectValue) return mSubjectValue; } } if (stackFrame != NULL) { if (mDebugTarget->GetValueByName(GetCurrentMethod(), findName, stackFrame, &valAddr, &valType, &addrType)) { //BF_ASSERT(valType != NULL); if (valType == NULL) { if (sizeof(addr_target) == 8) valType = mDbgModule->GetPrimitiveType(DbgType_i64, DbgLanguage_C); else valType = mDbgModule->GetPrimitiveType(DbgType_i32, DbgLanguage_C); } if (valType != NULL) { if (mReferenceId != NULL) *mReferenceId = findName; //bool isLocal = !valIsAddr; //if (valType->IsCompositeType()) //isLocal = false; return ReadTypedValue(identifierNode, valType, valAddr, addrType); } } } bool isClosure = false; if (language == DbgLanguage_Beef) { intptr valAddr; DbgType* valType; //bool valIsAddr = false; if (mDebugTarget->GetValueByName(GetCurrentMethod(), "__closure", stackFrame, &valAddr, &valType, &addrType)) { DbgTypedValue closureValue = ReadTypedValue(identifierNode, valType, valAddr, addrType); if (closureValue) { SetAndRestoreValue 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 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 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 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 argPushQueue; // auto curParam = mPropGet->mParams.mHead; // if (mPropGet->mHasThis) // { // argPushQueue.push_back(mPropTarget); // if (curParam != NULL) // curParam = curParam->mNext; // } // bool failed = false; // for (int indexerIdx = 0; indexerIdx < (int)mIndexerValues.size(); indexerIdx++) // { // auto val = mIndexerValues[indexerIdx]; // if (curParam != NULL) // { // val = Cast(mPropSrc, val, curParam->mType); // if (!val) // { // failed = true; // break; // } // } // argPushQueue.push_back(val); // // if (curParam != NULL) // curParam = curParam->mNext; // } // if (!failed) // { // if (mPropGet->mParams.Size() == argPushQueue.size()) // { // mResult = CreateCall(mPropGet, argPushQueue, false); // } // else // { // Fail("Indexer parameter count mismatch", mPropSrc); // } // } SetAndRestoreValue prevFlags(mExpressionFlags); if ((mExpressionFlags & DwEvalExpressionFlag_AllowPropertyEval) != 0) mExpressionFlags = (DwEvalExpressionFlags)(mExpressionFlags | DwEvalExpressionFlag_AllowCalls); mResult = CreateCall(mPropSrc, mPropTarget, mPropGet, false, mIndexerExprValues, mIndexerValues); } } } if ((mResult) && (mResult.mType->IsConst()) && (GetLanguage() == DbgLanguage_Beef)) { mResult.mIsReadOnly = true; // Pretend we don't really have the address } mPropGet = NULL; mPropSet = NULL; mPropSrc = NULL; mIndexerValues.clear(); mIndexerExprValues.clear(); return mResult; } bool DbgExprEvaluator::HasPropResult() { return (mPropGet != NULL) || (mPropSet != NULL); } DbgLanguage DbgExprEvaluator::GetLanguage() { if (mLanguage != DbgLanguage_NotSet) return mLanguage; if ((mDbgCompileUnit != NULL) && (mDbgCompileUnit->mLanguage != DbgLanguage_Unknown)) return mDbgCompileUnit->mLanguage; if (mExplicitThis) return mExplicitThis.mType->mLanguage; auto currentMethod = GetCurrentMethod(); if (currentMethod != NULL) return currentMethod->GetLanguage(); return DbgLanguage_Beef; // Default to Beef } DbgFlavor DbgExprEvaluator::GetFlavor() { if (mDbgCompileUnit != NULL) return mDbgCompileUnit->mDbgModule->mDbgFlavor; if (mExplicitThis) return mExplicitThis.mType->mCompileUnit->mDbgModule->mDbgFlavor; return DbgFlavor_Unknown; } void DbgExprEvaluator::AutocompleteAddMethod(const char* methodName, const StringImpl& filter) { const char* atPos = strchr(methodName, '@'); if (atPos == NULL) { if ((strncmp(methodName, "get__", 5) == 0) || (strncmp(methodName, "set__", 5) == 0)) { const char* propName = methodName + 5; if (*propName != (char)0) mAutoComplete->AddEntry(AutoCompleteEntry("property", propName), filter); } else if (strncmp(methodName, "__", 2) != 0) mAutoComplete->AddEntry(AutoCompleteEntry("method", methodName), filter); } } void DbgExprEvaluator::AutocompleteAddMembers(DbgType* dbgType, bool wantsStatic, bool wantsNonStatic, const StringImpl& filter, bool isCapture) { DbgLanguage language = GetLanguage(); DbgFlavor flavor = GetFlavor(); if ((mDbgCompileUnit != NULL) && (dbgType->mLanguage != DbgLanguage_Unknown) && (dbgType->mLanguage != language)) return; dbgType->PopulateType(); if (dbgType->mNeedsGlobalsPopulated) mDbgModule->PopulateTypeGlobals(dbgType); // Don't get primary type for namespace, causes mAlternates infinite loop if (!dbgType->IsNamespace()) dbgType = dbgType->GetPrimaryType(); // false/false means just add subtypes if ((wantsStatic) || (!wantsNonStatic)) { auto subType = dbgType->mSubTypeList.mHead; while (subType != NULL) { if (subType->mLanguage != language) { // Ignore } else if (subType->mTypeName == NULL) { AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter); } else { bool allowType = true; for (const char* cPtr = subType->mTypeName; *cPtr != '\0'; cPtr++) { char c = *cPtr; if ((c == '`') || (c == '[') || (c == '.') || (c == '$') || (c == '(')) allowType = false; } if ((allowType) && (subType->IsNamespace())) { // Is it a "using" declaration? if ((!dbgType->IsRoot()) && (!dbgType->IsNamespace())) allowType = false; } if ((!subType->IsAnonymous()) && (!subType->IsSizedArray()) && (allowType)) { mDbgModule->TempRemoveTemplateStr(subType->mTypeName, subType->mTemplateNameIdx); // We set the true mEntryType after the filter passes. This is because IsBfObject() is a "heavyweight" operation that we don't want to // just run on ALL types immediately AutoCompleteEntry* entry = mAutoComplete->AddEntry(AutoCompleteEntry("type", subType->mTypeName), filter); if (entry != NULL) { if (subType->IsBfObject()) entry->mEntryType = "class"; else if (subType->IsNamespace()) entry->mEntryType = "namespace"; else entry->mEntryType = "valuetype"; } mDbgModule->ReplaceTemplateStr(subType->mTypeName, subType->mTemplateNameIdx); } else if (subType->IsGlobalsContainer()) { if (subType->mPriority > DbgTypePriority_Normal) AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter); } if ((subType->mTypeCode == DbgType_Enum) && (language == DbgLanguage_C)) { AutocompleteAddMembers(subType, wantsStatic, wantsNonStatic, filter); } } subType = subType->mNext; } if (dbgType->mTypeCode == DbgType_Namespace) { for (auto altType : dbgType->mAlternates) { AutocompleteAddMembers(altType, wantsStatic, wantsNonStatic, filter); } } } // In beef, all globals are stored inside global containers so we can skip the rest bool skipMembers = (language == DbgLanguage_Beef) && (dbgType->mTypeCode == DbgType_Root); if (skipMembers) return; for (auto member : dbgType->mMemberList) { if (((member->mIsStatic) && (wantsStatic)) || ((!member->mIsStatic) && (wantsNonStatic))) { const char* name = member->mName; if (name != NULL) { if (member->mName[0] == '?') continue; mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(member->mType), name), filter); if ((isCapture) && (strcmp(member->mName, "__this") == 0)) { mAutoComplete->AddEntry(AutoCompleteEntry(GetTypeName(member->mType), "this"), filter); auto thisType = member->mType; if (thisType->IsPointer()) thisType = thisType->mTypeParam; thisType = thisType->RemoveModifiers(); AutocompleteAddMembers(thisType, wantsStatic, wantsNonStatic, filter); } } else { AutocompleteAddMembers(member->mType, wantsStatic, wantsNonStatic, filter); } } } if ((dbgType->IsNamespace()) || (dbgType->IsRoot())) { // We only add these ones because we can be certain they are all static, otherwise we need the full // method information which is in the compile units for (auto methodNameEntry : dbgType->mMethodNameList) { const char* methodName = methodNameEntry->mName; AutocompleteAddMethod(methodName, filter); } } else { //TODO: Not needed since we added the declarations now, right? //dbgType->EnsureMethodsMapped(); } for (auto method : dbgType->mMethodList) { if (((!method->mHasThis) && (wantsStatic)) || ((method->mHasThis) && (wantsNonStatic))) { if ((method->mName != NULL) && (strcmp(method->mName, "this") != 0)) { mDbgModule->FindTemplateStr(method->mName, method->mTemplateNameIdx); if (method->mTemplateNameIdx == -1) { AutocompleteAddMethod(method->mName, filter); } } } } /*if (!dbgType->mMethodNameList.IsEmpty()) { BF_ASSERT(!dbgType->mMethodList.IsEmpty()); }*/ /* for (auto methodNameEntry : dbgType->mMethodNameList) { const char* methodName = methodNameEntry->mName; AutocompleteAddMethod(methodName, filter); }*/ for (auto baseTypeEntry : dbgType->mBaseTypes) { AutocompleteAddMembers(baseTypeEntry->mBaseType, wantsStatic, wantsNonStatic, filter); } } void DbgExprEvaluator::AutocompleteCheckMemberReference(BfAstNode* target, BfAstNode* dotToken, BfAstNode* memberName) { if ((IsAutoCompleteNode(dotToken)) || (IsAutoCompleteNode(memberName))) { mDbgModule->ParseGlobalsData(); bool isCType = dotToken->ToString() == "::"; String filter; if (memberName != NULL) { filter = memberName->ToString(); mAutoComplete->mInsertStartIdx = memberName->GetSrcStart(); mAutoComplete->mInsertEndIdx = memberName->GetSrcEnd(); } else { mAutoComplete->mInsertStartIdx = dotToken->GetSrcEnd(); mAutoComplete->mInsertEndIdx = dotToken->GetSrcEnd(); } SetAndRestoreValue prevIgnoreError(mIgnoreErrors, true); if (target == NULL) { mResult.mType = GetExpectingType(); if (mResult.mType != NULL) mResult.mHasNoValue = true; } else if (auto typeRef = BfNodeDynCast(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(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(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 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 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(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(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 indexerExprValues; SizedArray indexerValues; for (auto indexExpr : indexerExpr->mArguments) { indexerExprValues.push_back(indexExpr); VisitChild(indexExpr); GetResult(); if (!mResult) indexerFailed = true; indexerValues.push_back(mResult); mResult = DbgTypedValue(); } if (indexerFailed) { mResult = DbgTypedValue(); return; } bool isBfArrayIndex = false; if ((collection.mType->IsBfObjectPtr()) || (collection.mType->IsStruct())) { DbgType* bfType = collection.mType->mTypeParam; if (bfType != NULL) { DbgType* baseType = bfType->GetBaseType(); if ((baseType != NULL) && (strcmp(baseType->mName, "System.Array") == 0)) { isBfArrayIndex = true; } } if (!isBfArrayIndex) { mIndexerExprValues = indexerExprValues; mIndexerValues = indexerValues; mResult = LookupField(indexerExpr->mTarget, collection, ""); if (HasPropResult()) { // Only use this if we actually have the method. Otherwise fall through so we can try a debug visualizer or something if ((mPropGet != NULL) && (mPropGet->mBlock.mLowPC != 0)) return; } // else // { // mResult = DbgTypedValue(); // Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket); // } // return; } } if (indexerValues.size() != 1) { Fail("Expected single index", indexerExpr->mOpenBracket); return; } DbgTypedValue indexArgument = indexerValues[0]; indexArgument.mType = indexArgument.mType->RemoveModifiers(); if (!indexArgument.mType->IsInteger()) { mResult = DbgTypedValue(); Fail("Expected integer index", indexerExpr->mArguments[0]); return; } if (mReferenceId != NULL) *mReferenceId += "[]"; if (isBfArrayIndex) { DbgType* bfType = collection.mType->mTypeParam; bfType = bfType->GetPrimaryType(); DbgType* baseType = bfType->GetBaseType(); DbgVariable* lenVariable = baseType->mMemberList.mHead; DbgVariable* typeVariable = bfType->mMemberList.mTail; if ((lenVariable != NULL) && (typeVariable != NULL)) { int len = mDebugger->ReadMemory(collection.mPtr + lenVariable->mMemberOffset); int idx = (int)indexArgument.GetInt64(); if ((idx < 0) || (idx >= len)) { Fail("Index out of range", indexerExpr->mArguments[0]); return; } auto result = ReadTypedValue(indexerExpr, typeVariable->mType, collection.mPtr + typeVariable->mMemberOffset + (idx * typeVariable->mType->GetStride()), DbgAddrType_Target); if (mResult.mIsReadOnly) result.mIsReadOnly = true; mResult = result; return; } } SetAndRestoreValue prevReferenceId(mReferenceId, NULL); Array 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 lowerDimSizes; if (sizeValue) { int len = (int)sizeValue.GetInt64(); int idx = (int)indexArgument.GetInt64(); if ((idx < 0) || (idx >= len)) { Fail("Index out of range", indexerExpr->mArguments[0]); return; } addr_target dataPtr = (collection.mType->IsPointer()) ? collection.mPtr : collection.mSrcAddress; String ptrUseDataTypeStr = collection.mType->ToStringRaw(); String ptrUseDataStr = StrFormat("(%s)", ptrUseDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true); //String evalStr = "(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + StrFormat("[%d]), this=", idx); String evalStr = "*(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + StrFormat(" + %d), this=", idx) + ptrUseDataStr; evalStr += ptrUseDataStr; auto result = mDebugger->EvaluateInContext(mDbgCompileUnit, collection, evalStr); if (mResult.mIsReadOnly) result.mIsReadOnly = true; mResult = result; if (mResult) return; } } } addr_target target = collection.mPtr; if (collection.mType->mTypeCode == DbgType_SizedArray) target = collection.mSrcAddress; if ((!collection.mType->IsPointer()) && (collection.mType->mTypeCode != DbgType_SizedArray)) { mResult = DbgTypedValue(); //Fail("Expected pointer type", indexerExpr->mTarget); Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket); return; } if (collection.mType->mTypeCode == DbgType_SizedArray) { int innerSize = collection.mType->mTypeParam->GetStride(); int len = 0; if (innerSize > 0) len = collection.mType->GetStride() / innerSize; int idx = (int)indexArgument.GetInt64(); if ((idx < 0) || (idx >= len)) { Fail("Index out of range", indexerExpr->mArguments[0]); return; } } if ((collection.mType->IsBfObjectPtr()) || (collection.mType->IsStruct())) { // This should have been handled by some other cases mResult = DbgTypedValue(); Fail(StrFormat("Unable to index type '%s'", TypeToString(collection.mType).c_str()), indexerExpr->mOpenBracket); } auto memberType = collection.mType->mTypeParam; auto result = ReadTypedValue(indexerExpr, memberType, target + indexArgument.GetInt64() * memberType->GetStride(), DbgAddrType_Target); if (mResult.mIsReadOnly) result.mIsReadOnly = true; mResult = result; } void DbgExprEvaluator::Visit(BfThisExpression* thisExpr) { if (mExplicitThis) { if (mExplicitThisExpr != NULL) mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(thisExpr, mCurChildRef)); mResult = mExplicitThis; /*if (mReferenceId != NULL) { auto checkType = mExplicitThis.mType; if (checkType->IsPointer()) checkType = checkType->mTypeParam; *mReferenceId = checkType->ToString(true); }*/ return; } bool hadError; mResult = LookupIdentifier(thisExpr, false, &hadError); if (!mResult) { if (GetCurrentMethod() != NULL) Fail("Static methods don't have 'this'", thisExpr); else Fail("Execution must be paused to retrieve 'this'", thisExpr); } } void DbgExprEvaluator::Visit(BfIdentifierNode* identifierNode) { //BfLogDbgExpr("Visit BfIdentifierNode %s\n", identifierNode->ToString().c_str()); mResult = LookupIdentifier(identifierNode, false, NULL); if (!mResult) { //?? //mDbgModule->EnsureMethodsMapped(identifierNode->ToString().c_str()); //mResult = LookupIdentifier(identifierNode, false, NULL); } if ((mResult) || (HasPropResult())) return; { SetAndRestoreValue 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(NULL, valType, valAddr, addrType); return; } } } } void DbgExprEvaluator::LookupSplatMember(BfAstNode* targetNode, BfAstNode* lookupNode, const DbgTypedValue& target, const StringImpl& fieldName, String* outFindName, bool* outIsConst, StringImpl* forceName) { /*DbgVariable* dbgVariable = (DbgVariable*)target.mVariable; if (dbgVariable != NULL) return LookupSplatMember(target, fieldName);*/ auto curMethod = GetCurrentMethod(); while (auto parenNode = BfNodeDynCast(lookupNode)) lookupNode = parenNode->mExpression; SplatLookupEntry* splatLookupEntry = NULL; if (lookupNode != NULL) { if (mSplatLookupMap.TryAdd(lookupNode, NULL, &splatLookupEntry)) { // } else if (forceName == NULL) { if (outFindName != NULL) *outFindName = splatLookupEntry->mFindName; if (outIsConst != NULL) *outIsConst = splatLookupEntry->mIsConst; mResult = splatLookupEntry->mResult; return; } } String findName; bool wasCast = false; mResult = DbgTypedValue(); BfAstNode* checkNode = targetNode; BfAstNode* parentNode = NULL; while (true) { if (checkNode == NULL) return; if (auto qualifiedNameNode = BfNodeDynCast(checkNode)) { parentNode = qualifiedNameNode->mLeft; findName = qualifiedNameNode->mRight->ToString(); break; } else if (auto memberRefExpr = BfNodeDynCast(checkNode)) { parentNode = memberRefExpr->mTarget; findName = memberRefExpr->mMemberName->ToString(); break; } else if (auto identifier = BfNodeDynCast(checkNode)) { findName = identifier->ToString(); break; } else if (auto thisExpr = BfNodeDynCast(checkNode)) { findName = thisExpr->ToString(); break; } else if (auto castExpr = BfNodeDynCast(checkNode)) { wasCast = true; checkNode = castExpr->mExpression; } else if (auto parenExpr = BfNodeDynCast(checkNode)) { checkNode = parenExpr->mExpression; } else return; } if (forceName != NULL) findName = *forceName; CPUStackFrame* stackFrame = GetStackFrame(); intptr valAddr; DbgType* valType = NULL; DbgAddrType addrType = DbgAddrType_Value; bool foundWantType = true; DbgType* wantType = target.mType; auto checkType = target.mType; checkType = checkType->RemoveModifiers(); bool valWasConst = false; bool foundParent = false; if (target.mSrcAddress == -1) { if (parentNode != NULL) { DbgTypedValue origTarget; origTarget.mSrcAddress = -1; origTarget.mType = target.mVariable->mType; origTarget.mVariable = target.mVariable; bool parentIsConst = false; String parentFindName; LookupSplatMember(parentNode, targetNode, origTarget, findName, &parentFindName, &parentIsConst); if (!parentFindName.IsEmpty()) { foundParent = true; findName = parentFindName; valType = mResult.mType; valWasConst = parentIsConst; } } } if (!foundParent) { if (!mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType)) return; if (addrType == DbgAddrType_Alias) { findName = (const char*)valAddr; if (!mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType)) { if (curMethod->mInlineeInfo != NULL) { // Look outside to inline SetAndRestoreValue prevStackIdx(mCallStackIdx, mCallStackIdx + 1); LookupSplatMember(targetNode, lookupNode, target, fieldName, outFindName, outIsConst, &findName); return; } return; } } if (addrType == DbgAddrType_LocalSplat) { DbgVariable* dbgVariable = (DbgVariable*)valAddr; // Use real name, in case of aliases findName = dbgVariable->mName; } } if ((wasCast) && (valType != NULL)) { checkType = valType->RemoveModifiers(); foundWantType = false; if (checkType->IsBfPayloadEnum()) { if (wasCast) { findName += "$u"; checkType = wantType; foundWantType = true; } } } mResult = DbgTypedValue(); if (valType != NULL) valWasConst |= valType->IsConst(); DbgType* memberType = NULL; bool found = false; bool wasUnion = false; while (checkType != NULL) { checkType = checkType->GetPrimaryType(); if ((!foundWantType) && (checkType->Equals(wantType))) foundWantType = true; if (foundWantType) { for (auto member : checkType->mMemberList) { if (member->mName == fieldName) { wasUnion = checkType->IsBfUnion(); memberType = member->mType; found = true; break; } } } if (found) break; checkType = checkType->GetBaseType(); findName += "$b"; } if (found) { bool wantConst = ((valWasConst) && (!memberType->IsConst()) && (!memberType->IsRef())); if (!findName.StartsWith("$")) findName = "$" + findName; if (wasUnion) findName += "$u"; else findName += "$m$" + fieldName; if (outFindName != NULL) *outFindName = findName; if (outIsConst != NULL) *outIsConst = wantConst; if (mDebugTarget->GetValueByName(curMethod, findName, stackFrame, &valAddr, &valType, &addrType)) { BF_ASSERT(valType != NULL); if (valType != NULL) { if (mReferenceId != NULL) *mReferenceId = findName; /*if ((valType->IsConst()) && (!memberType->IsConst())) { // Set readonly if the original value was const memberType = mDbgModule->GetConstType(valType); }*/ mResult = ReadTypedValue(targetNode, memberType, valAddr, addrType); if (wantConst) { // Set readonly if the original value was const mResult.mIsReadOnly = true; } } } else { if (target.mSrcAddress == -1) { // if (curMethod->mInlineeInfo != NULL) // { // // Look outside to inline // SetAndRestoreValue prevStackIdx(mCallStackIdx, mCallStackIdx + 1); // LookupSplatMember(targetNode, lookupNode, target, fieldName, outFindName, outIsConst, true); // return; // } if (!memberType->IsStruct()) Fail("Failed to lookup splat member", (lookupNode != NULL) ? lookupNode : targetNode); 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 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 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(nameNode->mLeft)) LookupQualifiedStaticField(qualifiedLeftName); else VisitChild(nameNode->mLeft); GetResult(); if (!mResult) { Fail("Identifier not found", nameNode->mLeft); return; } if (!mResult) return; auto checkType = mResult.mType->RemoveModifiers(); if (checkType->IsPointer()) checkType = checkType->mTypeParam; if ((checkType == NULL) || (!checkType->IsCompositeType())) { Fail("Type has no fields", nameNode->mLeft); return; } mResult = LookupField(nameNode, mResult, fieldName); if ((mResult) || (mPropSrc != NULL)) return; Fail("Unable to find member", nameNode->mRight); } bool DbgExprEvaluator::EnsureRunning(BfAstNode* astNode) { if (mDebugger->mIsRunning) return true; Fail("Target is not running", astNode); return false; } void DbgExprEvaluator::Visit(BfQualifiedNameNode* nameNode) { AutocompleteCheckMemberReference(nameNode->mLeft, nameNode->mDot, nameNode->mRight); bool hadError = false; LookupQualifiedName(nameNode, true, &hadError); if ((mResult) || (mPropSrc != NULL)) return; if (hadError) return; LookupQualifiedStaticField(nameNode); } void DbgExprEvaluator::Visit(BfDefaultExpression* defaultExpr) { mIsComplexExpression = true; /*auto type = mModule->ResolveTypeRef(defaultExpr->mTypeRef); if (!type) return; mResult = DbgTypedValue(mModule->GetDefaultValue(type), type);*/ } void DbgExprEvaluator::Visit(BfLiteralExpression* literalExpr) { mIsComplexExpression = true; mResult.mIsLiteral = true; auto language = GetLanguage(); switch (literalExpr->mValue.mTypeCode) { case BfTypeCode_NullPtr: { mResult.mPtr = 0; mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Null, GetLanguage()); } break; case BfTypeCode_CharPtr: mResult = GetString(*literalExpr->mValue.mString); break; case BfTypeCode_Boolean: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); mResult.mBool = literalExpr->mValue.mBool; break; case BfTypeCode_Char8: mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar : DbgType_SChar, language); mResult.mInt8 = literalExpr->mValue.mInt8; break; case BfTypeCode_Char16: mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar16 : DbgType_SChar16, language); mResult.mInt16 = literalExpr->mValue.mInt16; break; case BfTypeCode_Char32: mResult.mType = mDbgModule->GetPrimitiveType((language == DbgLanguage_Beef) ? DbgType_UChar32 : DbgType_SChar32, language); mResult.mInt32 = literalExpr->mValue.mInt32; break; case BfTypeCode_Int8: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i8, GetLanguage()); mResult.mUInt8 = literalExpr->mValue.mUInt8; break; case BfTypeCode_UInt8: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u8, GetLanguage()); mResult.mUInt8 = literalExpr->mValue.mUInt8; break; case BfTypeCode_Int16: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i16, GetLanguage()); mResult.mInt16 = literalExpr->mValue.mInt16; break; case BfTypeCode_UInt16: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u16, GetLanguage()); mResult.mUInt16 = literalExpr->mValue.mUInt16; break; case BfTypeCode_Int32: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); mResult.mInt32 = literalExpr->mValue.mInt32; break; case BfTypeCode_UInt32: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u32, GetLanguage()); mResult.mUInt32 = literalExpr->mValue.mUInt32; break; case BfTypeCode_Int64: case BfTypeCode_IntUnknown: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage()); mResult.mInt64 = literalExpr->mValue.mInt64; break; case BfTypeCode_UInt64: case BfTypeCode_UIntUnknown: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u64, GetLanguage()); mResult.mUInt64 = literalExpr->mValue.mUInt64; break; case BfTypeCode_IntPtr: if (sizeof(addr_target) == 8) { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage()); mResult.mInt64 = literalExpr->mValue.mInt64; } else { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); mResult.mInt32 = literalExpr->mValue.mInt32; } break; case BfTypeCode_UIntPtr: if (sizeof(addr_target) == 8) { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u64, GetLanguage()); mResult.mUInt64 = literalExpr->mValue.mUInt64; } else { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_u32, GetLanguage()); mResult.mUInt32 = literalExpr->mValue.mUInt32; } break; case BfTypeCode_Float: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Single, GetLanguage()); mResult.mSingle = literalExpr->mValue.mSingle; break; case BfTypeCode_Double: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Double, GetLanguage()); mResult.mDouble = literalExpr->mValue.mDouble; break; default: Fail("Invalid literal", literalExpr); break; } } void DbgExprEvaluator::Visit(BfCastExpression* castExpr) { mIsComplexExpression = true; if (castExpr->mTypeRef == NULL) return; // Error AutocompleteCheckType(castExpr->mTypeRef); auto resolvedType = ResolveTypeRef(castExpr->mTypeRef); if (resolvedType == NULL) return; if (mExplicitThisExpr != NULL) mDeferredInsertExplicitThisVector.push_back(NodeReplaceRecord(castExpr->mTypeRef, (BfAstNode**)&castExpr->mTypeRef)); mResult = CreateValueFromExpression(castExpr->mExpression); if (!mResult) return; mResult = Cast(castExpr, mResult, resolvedType, true); } // We pass by reference to support "RAW" debug expression simplification DbgTypedValue DbgExprEvaluator::CreateValueFromExpression(ASTREF(BfExpression*)& expr, DbgType* castToType, DbgEvalExprFlags flags) { // { BP_ZONE("DbgExprEvaluator::CreateValueFromExpression:CheckStack"); StackHelper stackHelper; if (!stackHelper.CanStackExpand(64 * 1024)) { DbgTypedValue result; if (!stackHelper.Execute([&]() { result = CreateValueFromExpression(expr, castToType, flags); })) { Fail("Expression too complex", expr); } return result; } } mExpectingType = castToType; VisitChild(expr); GetResult(); auto result = mResult; if ((result) && (castToType != NULL) && ((flags & DbgEvalExprFlags_NoCast) == 0)) result = Cast(expr, result, castToType, false); mResult = DbgTypedValue(); if ((result.mHasNoValue) && ((flags & DbgEvalExprFlags_AllowTypeResult) == 0)) Fail("Value expected", expr); return result; } void DbgExprEvaluator::PerformBinaryOperation(ASTREF(BfExpression*)& leftExpression, ASTREF(BfExpression*)& rightExpression, BfBinaryOp binaryOp, BfTokenNode* opToken, bool forceLeftType) { DbgTypedValue leftValue; if (leftExpression != NULL) leftValue = CreateValueFromExpression(leftExpression, mExpectingType, DbgEvalExprFlags_NoCast); DbgTypedValue rightValue; if (rightExpression == NULL) { return; } if (!leftValue) { VisitChild(rightExpression); return; } leftValue.mType = leftValue.mType->RemoveModifiers(); /*if (leftValue.mType->IsRef()) leftValue.mType = leftValue.mType->GetUnderlyingType();*/ if ((binaryOp == BfBinaryOp_ConditionalAnd) || (binaryOp == BfBinaryOp_ConditionalOr)) { bool isAnd = binaryOp == BfBinaryOp_ConditionalAnd; if (!leftValue.mType->IsBoolean()) { Fail(StrFormat("Operator requires boolean operand. Left hand side is '%s", leftValue.mType->ToString().c_str()), opToken); return; } auto boolType = leftValue.mType; if (isAnd) { if (!leftValue.mBool) { mResult = leftValue; return; } rightValue = CreateValueFromExpression(rightExpression); if (rightValue) rightValue = Cast(rightExpression, rightValue, boolType); mResult = rightValue; } else { if (leftValue.mBool) { mResult = leftValue; return; } rightValue = CreateValueFromExpression(rightExpression); if (rightValue) rightValue = Cast(rightExpression, rightValue, boolType); mResult = rightValue; } return; } if ((binaryOp == BfBinaryOp_NullCoalesce) && ((leftValue.mType->IsPointer()) || (leftValue.mType->IsBfObject()))) { if (leftValue.mPtr != 0) { mResult = leftValue; return; } CreateValueFromExpression(rightExpression, leftValue.mType); return; } rightValue = CreateValueFromExpression(rightExpression, mExpectingType, DbgEvalExprFlags_NoCast); if ((!leftValue) || (!rightValue)) return; rightValue.mType = rightValue.mType->RemoveModifiers(); if (leftValue.mType->IsTypedPrimitive()) leftValue.mType = leftValue.mType->GetUnderlyingType(); if (rightValue.mType->IsTypedPrimitive()) rightValue.mType = rightValue.mType->GetUnderlyingType(); // Prefer floats, prefer unsigned int leftCompareSize = leftValue.mType->GetByteCount(); if (leftValue.mType->IsFloat()) leftCompareSize += 16; if (!leftValue.mType->IsPrimitiveType()) leftCompareSize += 0x100; if ((leftValue.mIsLiteral) && (leftValue.mType->IsPointer())) leftCompareSize += 0x200; int rightCompareSize = rightValue.mType->GetByteCount(); if (rightValue.mType->IsFloat()) rightCompareSize += 16; if (!rightValue.mType->IsPrimitiveType()) rightCompareSize += 0x100; if ((rightValue.mIsLiteral) && (rightValue.mType->IsPointer())) rightCompareSize += 0x200; /*if ((leftValue.mType->IsTypedPrimitive()) && (rightValue.mType->IsTypedPrimitive())) { int leftInheritDepth = leftValue.mType->ToTypeInstance()->mInheritDepth; int rightInheritDepth = rightValue.mType->ToTypeInstance()->mInheritDepth; if (leftInheritDepth < rightInheritDepth) { // If both are typed primitives then choose the type with the lowest inherit depth // so we will choose the base type when applicable forceLeftType = true; } }*/ auto resultType = leftValue.mType; if (!forceLeftType) { if ((resultType->IsNull()) || (rightCompareSize > leftCompareSize) || (((rightCompareSize == leftCompareSize) && (!rightValue.mType->IsSigned()))) || (rightValue.mType->IsTypedPrimitive())) { // Select the type with the "most information" if (!rightValue.mType->IsNull()) resultType = rightValue.mType; } if ((!resultType->IsPointer()) && (rightValue.mType->IsPointer())) resultType = rightValue.mType; } //bool explicitCast = false; bool explicitCast = true; DbgTypedValue* resultTypedValue; DbgTypedValue* otherTypedValue; DbgType* otherType; BfAstNode* resultTypeSrc; BfAstNode* otherTypeSrc; if (resultType == leftValue.mType) { resultTypedValue = &leftValue; resultTypeSrc = leftExpression; otherTypedValue = &rightValue; otherTypeSrc = rightExpression; otherType = otherTypedValue->mType; } else { resultTypedValue = &rightValue; resultTypeSrc = rightExpression; otherTypedValue = &leftValue; otherTypeSrc = leftExpression; otherType = otherTypedValue->mType; } if ((resultTypedValue->mIsLiteral) && (resultType->IsPointer())) { // If we're comparing against a string literal like 'str == "Hey!"', handle that if (BfBinOpEqualityCheck(binaryOp)) { DbgType* useType = otherType; useType = useType->RemoveModifiers(); if (useType->IsPointer()) useType = useType->mTypeParam; DbgTypedValue useTypedValue = *otherTypedValue; if (useTypedValue.mType->IsPointer()) { useTypedValue.mSrcAddress = useTypedValue.mPtr; useTypedValue.mType = useTypedValue.mType->mTypeParam; } auto language = useType->GetLanguage(); auto dbgCompileUnit = useType->mCompileUnit; auto dbgModule = useType->GetDbgModule(); Array dbgVisWildcardCaptures; auto debugVis = mDebugger->FindVisualizerForType(useType, &dbgVisWildcardCaptures); if (debugVis != NULL) { auto& displayStringList = debugVis->mStringViews; String displayString; for (auto displayEntry : displayStringList) { auto& displayStringList = debugVis->mStringViews; DwFormatInfo formatInfo; formatInfo.mRawString = true; formatInfo.mLanguage = language; if (!displayEntry->mCondition.empty()) { if (!mDebugger->EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString)) continue; } String displayStr = mDebugger->mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures); mDebugger->ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, displayString, formatInfo, debugVis, false); bool isEq = displayString == resultTypedValue->mCharPtr; auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); mResult.mType = boolType; mResult.mBool = isEq == ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)); return; } } } Fail("Cannot perform operation with a literal value", resultTypeSrc); return; } DbgTypedValue convLeftValue; DbgTypedValue convRightValue; /*if ((resultType->IsVar()) || (otherType->IsVar())) { mResult = DbgTypedValue(mModule->GetDefaultValue(resultType), resultType); return; }*/ if ((otherType->IsNull()) && BfBinOpEqualityCheck(binaryOp)) { bool isEquality = (binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality); if (resultType->IsValueType()) { /*if (!mModule->IsInSpecializedGeneric()) { //CS0472: The result of the expression is always 'true' since a value of type 'int' is never equal to 'null' of type '' 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 args; BfResolvedArg leftArg; leftArg.mExpression = leftExpression; leftArg.mTypedValue = leftValue; args.push_back(leftArg); BfResolvedArg rightArg; rightArg.mExpression = rightExpression; rightArg.mTypedValue = rightValue; args.push_back(rightArg); BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL); BfBaseClassWalker baseClassWalker(leftValue.mType->ToTypeInstance(), rightValue.mType->ToTypeInstance()); bool invertResult = false; while (auto checkType = baseClassWalker.Next()) { BfOperatorDef* oppositeOperatorDef = NULL; BfBinaryOp oppositeBinaryOp = GetOppositeBinaryOp(binaryOp); for (auto operatorDef : checkType->mTypeDef->mOperators) { if (operatorDef->mOperatorDeclaration->mBinOp == binaryOp) methodMatcher.CheckMethod(checkType, operatorDef); else if (operatorDef->mOperatorDeclaration->mBinOp == oppositeBinaryOp) oppositeOperatorDef = operatorDef; } if ((methodMatcher.mBestMethodDef == NULL) && (oppositeOperatorDef != NULL)) { if (methodMatcher.CheckMethod(checkType, oppositeOperatorDef)) invertResult = true; } } if (methodMatcher.mBestMethodDef != NULL) { SetElementType(opToken, BfSourceElementType_Method); mResult = CreateCall(&methodMatcher, DbgTypedValue()); if ((invertResult) && (mResult.mType == mModule->GetPrimitiveType(BfTypeCode_Boolean))) mResult.mValue = mModule->mIRBuilder->CreateNot(mResult.mValue); return; } }*/ if (resultType->IsPointer() && otherType->IsPointer()) { //TODO: Allow all pointer comparisons, but only allow SUBTRACTION between equal pointer types if (binaryOp == BfBinaryOp_Subtract) { if (!resultType->Equals(otherType)) { Fail(StrFormat("Operands must be the same type, '%s' doesn't match '%s'", leftValue.mType->ToString().c_str(), rightValue.mType->ToString().c_str()), opToken); return; } DbgType* intPtrType = mDbgModule->GetPrimitiveType(DbgType_IntPtr_Alias, GetLanguage()); long ptrDiff = leftValue.mPtr - rightValue.mPtr; int elemSize = resultType->mTypeParam->GetStride(); if (elemSize == 0) { Fail("Invalid operation on void values", opToken); return; } //BF_ASSERT((ptrDiff % elemSize) == 0); mResult.mInt64 = ptrDiff / elemSize; mResult.mType = intPtrType; return; } else if ((binaryOp != BfBinaryOp_Equality) && (binaryOp != BfBinaryOp_StrictEquality) & (binaryOp != BfBinaryOp_InEquality) && (binaryOp != BfBinaryOp_StrictInEquality) && (binaryOp != BfBinaryOp_LessThan) && (binaryOp != BfBinaryOp_LessThanOrEqual) && (binaryOp != BfBinaryOp_GreaterThan) && (binaryOp != BfBinaryOp_GreaterThanOrEqual)) { Fail("Invalid operation on pointers", opToken); return; } // Compare them as ints resultType = mDbgModule->GetPrimitiveType(DbgType_UIntPtr_Alias, GetLanguage()); explicitCast = true; } else if (resultType->IsPointer()) { if (otherType->IsNull()) { if (!BfBinOpEqualityCheck(binaryOp)) { Fail(StrFormat("Invalid operation between '%s' and null", resultType->ToString().c_str()), opToken); return; } mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (binaryOp == BfBinaryOp_Equality) mResult.mBool = resultTypedValue->mPtr == NULL; else mResult.mBool = resultTypedValue->mPtr != NULL; return; } bool isCompare = (binaryOp >= BfBinaryOp_Equality) && (binaryOp <= BfBinaryOp_LessThanOrEqual); // One pointer if ((!otherType->IsInteger()) || ((binaryOp != BfBinaryOp_Add) && (binaryOp != BfBinaryOp_Subtract) && (!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 (!BfBinOpEqualityCheck(binaryOp)) { if (resultType->IsPointer()) Fail("Invalid operation for pointers", opToken); else Fail("Invalid operation for objects", opToken); return; } if (otherType->IsNull()) { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)) mResult.mBool = resultTypedValue->mPtr == NULL; else mResult.mBool = resultTypedValue->mPtr != NULL; } else { bool explicitCast = true; if (otherType->IsInteger()) explicitCast = true; auto convertedValue = Cast(otherTypeSrc, *otherTypedValue, resultType, explicitCast); if (!convertedValue) return; mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)) mResult.mBool = resultTypedValue->mPtr == convertedValue.mPtr; else mResult.mBool = resultTypedValue->mPtr != convertedValue.mPtr; } return; } /*else if (resultType->IsTypedPrimitive()) { bool needsOtherCast = true; if (otherType != resultType) { if (otherType->IsPrimitiveType()) { // Allow zero comparisons to match all typed primitives if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_InEquality)) { auto intConstant = dyn_cast(otherTypedValue->mValue); if (intConstant != NULL) { if (intConstant->getSExtValue() == 0) { needsOtherCast = false; } } } } if (needsOtherCast) { // The only purpose of this cast is to potentially throw a casting error Value* otherCastResult = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, explicitCast); if (otherCastResult == NULL) return; } } auto underlyingType = resultType->GetUnderlyingType(); Value* convResultValue = mModule->CastToValue(resultTypeSrc, *resultTypedValue, underlyingType, true); Value* convOtherValue = mModule->CastToValue(otherTypeSrc, *otherTypedValue, underlyingType, true); if ((!underlyingType->IsValuelessType()) && ((convResultValue == NULL) || (convOtherValue == NULL))) return; if (resultTypedValue == &leftValue) PerformBinaryOperation(underlyingType, convResultValue, convOtherValue, binaryOp, opToken); else PerformBinaryOperation(underlyingType, convOtherValue, convResultValue, binaryOp, opToken); if (mResult.mType == underlyingType) mResult.mType = resultType; return; }*/ else if (((leftValue.mType->IsStruct()) || (leftValue.mType->IsBfObject())) && (!resultType->IsEnum())) { if (leftValue.mType == rightValue.mType) { Fail(StrFormat("Operator '%s' cannot be applied to operands of type '%s'", opToken->ToString().c_str(), TypeToString(leftValue.mType).c_str()), opToken); } else { Fail(StrFormat("Operator '%s' cannot be applied to operands of type '%s' and '%s'", opToken->ToString().c_str(), TypeToString(leftValue.mType).c_str(), TypeToString(rightValue.mType).c_str()), opToken); } return; } if ((resultType->IsInteger()) && (!forceLeftType)) { bool isBitwiseExpr = (binaryOp == BfBinaryOp_BitwiseAnd) | (binaryOp == BfBinaryOp_BitwiseOr) | (binaryOp == BfBinaryOp_ExclusiveOr) | (binaryOp == BfBinaryOp_LeftShift) | (binaryOp == BfBinaryOp_RightShift); // For binary expressions we don't promote to 'int' unless the types don't match if ((!isBitwiseExpr) || (leftValue.mType != rightValue.mType)) { if (binaryOp == BfBinaryOp_Subtract) { } else if (resultType->GetByteCount() < 4) { resultType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); } else if (leftValue.mType != rightValue.mType) { if ((binaryOp == BfBinaryOp_LeftShift) || (binaryOp == BfBinaryOp_RightShift)) { // For shifts we leave it in the target type if we're already at 'int' or higher } else if ((!resultType->IsSigned()) && (otherType->IsSigned())) { /*if (CanCast(*otherTypedValue, resultType)) { // If we can convert the 'other' value implicitly then it's a convertible literal, leave as uint } else if (resultType->mSize == 4) { resultType = mDbgModule->GetPrimitiveType(DbgType_i64); } else { Fail(StrFormat("Operator cannot be applied to operands of type '%s' and '%s'", TypeToString(leftValue.mType).c_str(), TypeToString(rightValue.mType).c_str()), opToken); return; }*/ explicitCast = true; } } } } if (convLeftValue == NULL) convLeftValue = Cast(leftExpression, leftValue, resultType, explicitCast); if (convRightValue == NULL) convRightValue = Cast(rightExpression, rightValue, resultType, explicitCast); PerformBinaryOperation(resultType, convLeftValue, convRightValue, binaryOp, opToken); } void DbgExprEvaluator::PerformBinaryOperation(DbgType* resultType, DbgTypedValue convLeftValue, DbgTypedValue convRightValue, BfBinaryOp binaryOp, BfTokenNode* opToken) { if (resultType->IsValuelessType()) { switch (binaryOp) { case BfBinaryOp_Equality: case BfBinaryOp_StrictEquality: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); mResult.mBool = true; break; case BfBinaryOp_InEquality: case BfBinaryOp_StrictInEquality: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); mResult.mBool = false; break; default: Fail("Invalid operation for void", opToken); break; } return; } if ((convLeftValue == NULL) || (convRightValue == NULL)) return; if ((resultType->IsEnum()) || (resultType->IsTypedPrimitive())) resultType = resultType->GetUnderlyingType(); if (resultType->IsPrimitiveType()) { auto primType = resultType; if (primType->mTypeCode == DbgType_Bool) { mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); switch (binaryOp) { case BfBinaryOp_Equality: case BfBinaryOp_StrictEquality: mResult.mBool = convLeftValue.mBool == convRightValue.mBool; break; case BfBinaryOp_InEquality: case BfBinaryOp_StrictInEquality: mResult.mBool = convLeftValue.mBool != convRightValue.mBool; break; case BfBinaryOp_ConditionalAnd: mResult.mBool = convLeftValue.mBool && convRightValue.mBool; break; case BfBinaryOp_ConditionalOr: mResult.mBool = convLeftValue.mBool | convRightValue.mBool; break; case BfBinaryOp_ExclusiveOr: mResult.mBool = convLeftValue.mBool ^ convRightValue.mBool; break; default: Fail("Invalid operation for booleans", opToken); break; } return; } } if ((!resultType->IsIntegral()) && (!resultType->IsFloat())) { Fail(StrFormat("Cannot perform operation on type '%s'", TypeToString(resultType).c_str()), opToken); return; } mResult.mType = resultType; if (resultType->IsInteger()) { switch (binaryOp) { case BfBinaryOp_BitwiseAnd: mResult.mInt64 = convLeftValue.GetInt64() & convRightValue.GetInt64(); return; case BfBinaryOp_BitwiseOr: mResult.mInt64 = convLeftValue.GetInt64() | convRightValue.GetInt64(); return; case BfBinaryOp_ExclusiveOr: mResult.mInt64 = convLeftValue.GetInt64() ^ convRightValue.GetInt64(); return; case BfBinaryOp_LeftShift: mResult.mInt64 = convLeftValue.GetInt64() << convRightValue.GetInt64(); return; case BfBinaryOp_RightShift: mResult.mInt64 = convLeftValue.GetInt64() >> convRightValue.GetInt64(); return; } } switch (binaryOp) { case BfBinaryOp_Add: 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: case BfBinaryOp_StrictEquality: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle == convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble == convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() == convRightValue.GetInt64(); break; case BfBinaryOp_InEquality: case BfBinaryOp_StrictInEquality: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle != convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble != convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() != convRightValue.GetInt64(); break; case BfBinaryOp_LessThan: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle < convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble < convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() < convRightValue.GetInt64(); break; case BfBinaryOp_LessThanOrEqual: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle <= convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble <= convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() <= convRightValue.GetInt64(); break; case BfBinaryOp_GreaterThan: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle > convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble > convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() > convRightValue.GetInt64(); break; case BfBinaryOp_GreaterThanOrEqual: mResult.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (resultType->mTypeCode == DbgType_Single) mResult.mBool = convLeftValue.mSingle >= convRightValue.mSingle; else if (resultType->mTypeCode == DbgType_Double) mResult.mBool = convLeftValue.mDouble >= convRightValue.mDouble; else mResult.mBool = convLeftValue.GetInt64() >= convRightValue.GetInt64(); break; default: Fail("Invalid operation", opToken); break; } } void DbgExprEvaluator::Visit(BfBinaryOperatorExpression* binOpExpr) { //mIsComplexExpression = true; //PerformBinaryOperation(binOpExpr->mLeft, binOpExpr->mRight, binOpExpr->mOp, binOpExpr->mOpToken, false); // Check for "(double)-1.0" style cast, misidentified as a binary operation if ((binOpExpr->mOp == BfBinaryOp_Add) || (binOpExpr->mOp == BfBinaryOp_Subtract) || (binOpExpr->mOp == BfBinaryOp_BitwiseAnd) || (binOpExpr->mOp == BfBinaryOp_Multiply)) { if (auto parenExpr = BfNodeDynCast(binOpExpr->mLeft)) { BfAstNode* castTypeExpr = BfNodeDynCast(parenExpr->mExpression); if (castTypeExpr == NULL) castTypeExpr = BfNodeDynCast(parenExpr->mExpression); if (castTypeExpr != NULL) { SetAndRestoreValue prevIgnoreError(mIgnoreErrors, true); auto resolvedType = ResolveTypeRef(castTypeExpr); prevIgnoreError.Restore(); if (resolvedType != NULL) { if (binOpExpr->mOp == BfBinaryOp_BitwiseAnd) { PerformUnaryExpression(binOpExpr->mOpToken, BfUnaryOp_AddressOf, binOpExpr->mRight); } else if (binOpExpr->mOp == BfBinaryOp_Multiply) { PerformUnaryExpression(binOpExpr->mOpToken, BfUnaryOp_Dereference, binOpExpr->mRight); } else { VisitChild(binOpExpr->mRight); if (!mResult) return; } if ((mResult) && (binOpExpr->mOp == BfBinaryOp_Subtract)) { if (mResult.mType->mTypeCode == DbgType_Single) mResult.mSingle = -mResult.mSingle; else if (mResult.mType->mTypeCode == DbgType_Double) mResult.mDouble = -mResult.mDouble; else mResult.mInt64 = -mResult.GetInt64(); } mResult = Cast(binOpExpr, mResult, resolvedType, true); return; } } } } if (binOpExpr->mRight == NULL) { //mModule->AssertErrorState(); // We visit the children for autocompletion only if (binOpExpr->mLeft != NULL) VisitChild(binOpExpr->mLeft); if (mResult) { //if (mAutoComplete != NULL) //mAutoComplete->CheckEmptyStart(binOpExpr->mOpToken, mResult.mType); } if (binOpExpr->mRight != NULL) VisitChild(binOpExpr->mRight); return; } PerformBinaryOperation(binOpExpr->mLeft, binOpExpr->mRight, binOpExpr->mOp, binOpExpr->mOpToken, false); } void DbgExprEvaluator::PerformUnaryExpression(BfAstNode* opToken, BfUnaryOp unaryOp, ASTREF(BfExpression*)& expr) { if (unaryOp != BfUnaryOp_Dereference) mIsComplexExpression = true; VisitChild(expr); GetResult(); if (!mResult) return; bool wasLiteral = mResult.mIsLiteral; mResult.mIsLiteral = false; switch (unaryOp) { case BfUnaryOp_Decrement: case BfUnaryOp_PostDecrement: case BfUnaryOp_Increment: case BfUnaryOp_PostIncrement: auto ptr = mResult; ptr.mType = ptr.mType->RemoveModifiers(); if (ptr.mType->IsEnum()) ptr.mType = ptr.mType->mTypeParam; auto val = mResult; int add = ((unaryOp == BfUnaryOp_Decrement) || (unaryOp == BfUnaryOp_PostDecrement)) ? -1 : 1; switch (ptr.mType->mTypeCode) { case DbgType_UChar: case DbgType_SChar: case DbgType_i8: case DbgType_u8: case DbgType_Utf8: val.mUInt8 += add; break; case DbgType_UChar16: case DbgType_SChar16: case DbgType_i16: case DbgType_u16: case DbgType_Utf16: val.mUInt16 += add; break; case DbgType_UChar32: case DbgType_SChar32: case DbgType_i32: case DbgType_u32: case DbgType_Utf32: val.mUInt32 += add; break; case DbgType_i64: case DbgType_u64: val.mUInt64 += add; break; case DbgType_Single: val.mSingle += add; break; case DbgType_Double: val.mDouble += add; break; default: Fail(StrFormat("Unable to perform operation on type '%s'", ptr.mType->ToString()), expr); return; } if ((mExpressionFlags & DwEvalExpressionFlag_AllowSideEffects) == 0) { mBlockedSideEffects = true; return; } else { StoreValue(mResult, val, opToken); } if ((unaryOp == BfUnaryOp_Decrement) || (unaryOp == BfUnaryOp_Increment)) mResult = val; return; } bool numericFail = false; switch (unaryOp) { case BfUnaryOp_Dereference: { auto type = mResult.mType->RemoveModifiers(); if (!type->IsPointer()) { Fail("Operator can only be used on pointer values", opToken); return; } mResult = ReadTypedValue(opToken, type->mTypeParam, mResult.mPtr, DbgAddrType_Target); } break; case BfUnaryOp_AddressOf: { if ((mResult.mSrcAddress == 0) || (mResult.mSrcAddress == -1)) { if (mResult.mRegNum != -1) { Fail(StrFormat("Cannot take address of register '%s'", CPURegisters::GetRegisterName(mResult.mRegNum)), opToken); return; } Fail("Cannot take address of value", opToken); return; } mResult.mType = mResult.mType->RemoveModifiers(); mResult.mType = mDbgModule->GetPointerType(mResult.mType); mResult.mPtr = mResult.mSrcAddress; mResult.mSrcAddress = 0; } break; case BfUnaryOp_Not: { auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); if (mResult.mType->mTypeCode != DbgType_Bool) { Fail("Operator can only be used on boolean values", opToken); return; } mResult.mIsLiteral = wasLiteral; mResult.mBool = !mResult.mBool; } break; case BfUnaryOp_Negate: { if (mResult.mType->IsInteger()) { auto primType = mResult.mType; auto wantType = primType; if (wasLiteral) { // This is a special case where the user entered -0x80000000 (maxint) but we thought "0x80000000" was a uint in the parser // which would get expanded to an int64 for this negate. Properly bring back down to an int32 if ((primType->mTypeCode == DbgType_u32) && (mResult.GetInt64() == -0x80000000LL)) { //mResult = DbgTypedValue(mModule->GetConstValue((int) i64Val), mModule->GetPrimitiveType(DwTypeCode_Int32)); mResult.mType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); return; } } if (!primType->IsSigned()) { if (primType->mSize == 1) wantType = mDbgModule->GetPrimitiveType(DbgType_i16, GetLanguage()); else if (primType->mSize == 2) wantType = mDbgModule->GetPrimitiveType(DbgType_i32, GetLanguage()); else if (primType->mSize == 4) wantType = mDbgModule->GetPrimitiveType(DbgType_i64, GetLanguage()); } mResult.mIsLiteral = wasLiteral; mResult.mInt64 = -mResult.GetInt64(); mResult.mType = wantType; return; } mResult.mIsLiteral = wasLiteral; if (mResult.mType->mTypeCode == DbgType_Single) mResult.mSingle = -mResult.mSingle; else if (mResult.mType->mTypeCode == DbgType_Double) mResult.mDouble = -mResult.mDouble; else numericFail = true; } break; case BfUnaryOp_InvertBits: { mResult.mIsLiteral = wasLiteral; if (mResult.mType->IsInteger()) { switch (mResult.mType->mSize) { case 1: mResult.mUInt8 = ~mResult.mUInt8; break; case 2: mResult.mUInt16 = ~mResult.mUInt16; break; case 4: mResult.mUInt32 = ~mResult.mUInt32; break; case 8: mResult.mUInt64 = ~mResult.mUInt64; break; } } } break; default: Fail("Invalid operator for debug expression", opToken); break; } if (numericFail) { Fail("Operator can only be used on numeric types", opToken); mResult = DbgTypedValue(); } } void DbgExprEvaluator::Visit(BfUnaryOperatorExpression* unaryOpExpr) { PerformUnaryExpression(unaryOpExpr->mOpToken, unaryOpExpr->mOp, unaryOpExpr->mExpression); } bool DbgExprEvaluator::ResolveArgValues(const BfSizedArray& arguments, SizedArrayImpl& 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(NULL, 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(thisPtr); if (isBfObject) { mDebugTarget->GetCompilerSettings(); if (mDebugTarget->mBfObjectHasFlags) vtablePtr &= ~0xFF; } if (method->mVTableLoc >= 0x100000) { // Virtual ext method, double indirection addr_target extAddr = mDebugger->ReadMemory(vtablePtr + (uint)((method->mVTableLoc>>20) - 1)*sizeof(addr_target)); startAddr = mDebugger->ReadMemory(extAddr + (uint)(method->mVTableLoc & 0xFFFFF)*sizeof(addr_target)); } else startAddr = mDebugger->ReadMemory(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& arguments, SizedArrayImpl& argValues) { HashSet splatParams; SizedArray argPushQueue; // { for (auto variable : method->mBlock.mVariables) { if ((variable->mName == NULL) && (variable->mName[0] != '$')) continue; const char* dollarPos = strchr(variable->mName + 1, '$'); if (dollarPos == NULL) continue; String varName = String(variable->mName + 1, dollarPos - variable->mName - 1); splatParams.Add(varName); } } int argIdx = 0; int paramIdx = 0; auto _PushArg = [&](const DbgTypedValue& typedValue, DbgVariable* param) { if (typedValue.mType->GetByteCount() == 0) return; if ((param != NULL) && (param->mType != NULL) && (param->mType->IsCompositeType())) { if ((param->mName != NULL) && (splatParams.Contains(param->mName))) { std::function _SplatArgs = [&](const DbgTypedValue& typedVal) { auto type = typedVal.mType->RemoveModifiers(); type = type->GetPrimaryType(); if (type->IsValuelessType()) return; if (!type->IsCompositeType()) { auto elemTypedValue = ReadTypedValue(targetSrc, type, typedVal.mSrcAddress, DbgAddrType_Target); DbgMethodArgument methodArg; methodArg.mTypedValue = elemTypedValue; argPushQueue.push_back(methodArg); return; } auto baseType = type->GetBaseType(); if (baseType != NULL) { DbgTypedValue baseValue = typedVal; baseValue.mType = baseType; _SplatArgs(baseValue); } for (auto member : type->mMemberList) { if ((member->mIsStatic) || (member->mIsConst)) continue; DbgTypedValue memberValue; memberValue.mSrcAddress = typedVal.mSrcAddress + member->mMemberOffset; memberValue.mType = member->mType; _SplatArgs(memberValue); } }; _SplatArgs(typedValue); return; } } DbgMethodArgument methodArg; methodArg.mTypedValue = typedValue; methodArg.mWantsRef = param->mType->IsRef(); argPushQueue.push_back(methodArg); }; 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->CanCast(argValues[argIdx], wantType)) isDirectPass = true; } if (!isDirectPass) { BfArrayType* arrayType = (BfArrayType*) wantType; if (arrayType->IsIncomplete()) mModule->PopulateType(arrayType, true); expandedParamsElementType = arrayType->mTypeGenericArguments[0]; int arrayClassSize = arrayType->mInstSize - expandedParamsElementType->mSize; int numElements = (int) arguments.size() - argIdx; Value* sizeValue = mModule->GetConstValue(arrayClassSize); Value* elementDataSize = mModule->mIRBuilder->CreateMul(mModule->GetConstValue(expandedParamsElementType->mSize), mModule->GetConstValue(numElements)); sizeValue = mModule->mIRBuilder->CreateAdd(sizeValue, elementDataSize); auto paramsData = mModule->GetHeadIRBuilder()->CreateAlloca(Type::getInt8Ty(*mModule->mLLVMContext), sizeValue, "paramsData"); expandedParamsArray = DbgTypedValue(mModule->mIRBuilder->CreateBitCast(paramsData, arrayType->mLLVMType), arrayType, false); MatchConstructor(targetSrc, expandedParamsArray, arrayType, SizedArray(), 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& 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(®isters); 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, ®isters, 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(®isters, thisAddr); } else { mDebugger->AddParamValue(paramIdx, method->mHasThis, ®isters, arg.mTypedValue); paramIdx--; } } #ifdef BF_DBG_64 // Stack must be 16-byte aligned if ((*regSP & 0xF) == 0) break; #else break; #endif registers = regSave; paramIdx = paramIdxSave; BF_ASSERT(padCount < 3); } return CreateCall(method, thisVal, bypassVirtual, ®isters); } DbgTypedValue DbgExprEvaluator::MatchMethod(BfAstNode* targetSrc, DbgTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& methodName, const BfSizedArray& arguments, BfSizedArray* methodGenericArguments) { SetAndRestoreValue prevReferenceId(mReferenceId, NULL); bool wantCtor = methodName.IsEmpty(); mDbgModule->ParseGlobalsData(); //mDbgModule->ParseSymbolData(); SizedArray argValues; if (!ResolveArgValues(arguments, argValues)) return DbgTypedValue(); if ((methodName == "__cast") || (methodName == "__bitcast")) { if (argValues.size() > 0) { String typeName = ""; for (int argIdx = 0; argIdx < (int)argValues.size() - 1; argIdx++) { auto arg = argValues[argIdx]; if (!arg) return DbgTypedValue(); if ((arg.mType->IsPointer()) && ((arg.mType->mTypeParam->mTypeCode == DbgType_UChar) || (arg.mType->mTypeParam->mTypeCode == DbgType_SChar))) { typeName += arg.mCharPtr; } else if ((arg.mType->mTypeCode == DbgType_i32) || (arg.mType->mTypeCode == DbgType_i64)) { typeName += BfTypeUtils::HashEncode64(arg.mInt64); } else { BeefTypeToString(arg, typeName); } } auto castedType = mDbgModule->FindType(typeName, NULL, GetLanguage(), true); if (castedType == NULL) { if (typeName.EndsWith('*')) { auto noPtrTypeEntry = mDbgModule->FindType(typeName.Substring(0, typeName.length() - 1), NULL, DbgLanguage_Beef, true); if (noPtrTypeEntry != NULL) castedType = mDbgModule->GetPointerType(noPtrTypeEntry); } } int targetArgIdx = argValues.size() - 1; if (castedType == NULL) { Fail(StrFormat("Unable to find type: '%s'", typeName.c_str()), targetSrc); return argValues[targetArgIdx]; } if (methodName == "__bitcast") { DbgTypedValue result = argValues[targetArgIdx]; result.mType = castedType; return result; } return Cast(arguments[targetArgIdx], argValues[targetArgIdx], castedType, true); } } else if (methodName == "__hasField") { if (argValues.size() == 2) { auto checkType = argValues[0].mType; if ((checkType != NULL) && (checkType->IsPointer())) checkType = checkType->mTypeParam; if (checkType != NULL) { //TODO: Protect String findFieldName = argValues[1].mCharPtr; DbgTypedValue result; result.mType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); result.mBool = HasField(checkType, findFieldName); return result; } } } else if (methodName == "__desc") { if (argValues.size() == 1) { auto checkType = argValues[0].mType; if ((checkType != NULL) && (checkType->IsPointer())) checkType = checkType->mTypeParam; if (checkType != NULL) { checkType = checkType->GetPrimaryType(); String dsc = "DESCRIPTION OF "; dsc += checkType->ToString(); dsc += "\n"; dsc += "From Compile Unit: "; dsc += checkType->mCompileUnit->mName; dsc += "\n"; dsc += StrFormat("Size: %d\n", checkType->GetByteCount()); dsc += StrFormat("Align: %d\n", checkType->GetAlign()); for (auto member : checkType->mMemberList) { dsc += " "; if (member->mIsConst) dsc += "const "; if (member->mIsExtern) dsc += "extern "; if (member->mIsStatic) dsc += "static "; dsc += member->mType->ToString(); dsc += " "; dsc += member->mName; if (!member->mIsStatic) { dsc += StrFormat(" Offset:%d", member->mMemberOffset); } dsc += StrFormat(" Size:%d", member->mType->GetByteCount()); if (member->mLinkName != NULL) dsc += member->mLinkName; dsc += "\n"; } if ((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0) mDebugger->OutputMessage(dsc); return DbgTypedValue(); } } } else if (methodName == "__demangleMethod") { if (argValues.size() == 2) { auto checkType = argValues[0].mType; if (checkType->IsPointer()) checkType = checkType->mTypeParam; //TODO: Protect String findMethodName = argValues[1].mCharPtr; checkType->PopulateType(); for (auto checkMethod : checkType->mMethodList) { if (checkMethod->mName == findMethodName) { static String demangledName; demangledName = BfDemangler::Demangle(checkMethod->mLinkName, checkType->GetLanguage()); DbgTypedValue result; result.mType = argValues[1].mType; result.mCharPtr = demangledName.c_str(); result.mIsLiteral = true; return result; } } } } else if (methodName == "__demangle") { if (argValues.size() == 1) { auto rawTextType = mDbgModule->GetPrimitiveType(DbgType_RawText, GetLanguage()); String mangledName = argValues[0].mCharPtr; static String demangledName; demangledName = BfDemangler::Demangle(mangledName, DbgLanguage_Unknown); if (demangledName.StartsWith("bf.")) demangledName.Remove(0, 3); DbgTypedValue result; result.mType = rawTextType; result.mCharPtr = demangledName.c_str(); result.mIsLiteral = true; return result; } } else if (methodName == "__demangleFakeMember") { if (argValues.size() == 1) { auto checkType = argValues[0].mType; if (checkType->IsPointer()) checkType = checkType->mTypeParam; auto rawTextType = mDbgModule->GetPrimitiveType(DbgType_RawText, GetLanguage()); checkType->PopulateType(); auto firstMember = checkType->mMemberList.mHead; static String demangledName; demangledName = BfDemangler::Demangle(firstMember->mName, checkType->GetLanguage()); if (demangledName.StartsWith("bf.")) demangledName.Remove(0, 3); DbgTypedValue result; result.mType = rawTextType; result.mCharPtr = demangledName.c_str(); result.mIsLiteral = true; return result; } } else if (methodName == "__funcName") { if (argValues.size() == 1) { auto funcPtr = argValues[0].mPtr; String symbolName; addr_target offset; DbgModule* dwarf; static String demangledName; auto subProgram = mDebugTarget->FindSubProgram(funcPtr); if (subProgram != NULL) { demangledName = subProgram->ToString(); } else if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf)) { demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Beef); } else { demangledName = StrFormat("0x%@", funcPtr); } DbgTypedValue result; result.mType = mDbgModule->GetPrimitiveType(DbgType_Subroutine, GetLanguage()); result.mCharPtr = demangledName.c_str(); return result; } } else if (methodName == "__funcTarget") { if (argValues.size() == 2) { auto funcPtr = argValues[0].mPtr; auto funcTarget = argValues[1].mPtr; String symbolName; addr_target offset; DbgModule* dwarf; if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf)) { String firstParamType = BfDemangler::Demangle(symbolName, DbgLanguage_Beef, BfDemangler::Flag_CaptureTargetType); auto targetType = mDbgModule->FindType(firstParamType, NULL, DbgLanguage_BeefUnfixed); if (targetType) { if (!targetType->IsPointer()) targetType = mDbgModule->GetPointerType(targetType); DbgTypedValue result; result.mType = targetType; result.mPtr = funcTarget; return result; } } return argValues[1]; } } else if (methodName == "__stringView") { if (argValues.size() >= 2) { if ((argValues[1].mType != NULL) && (argValues[1].mType->IsInteger())) mCountResultOverride = (intptr)argValues[1].GetInt64(); return argValues[0]; } } else if (methodName == "__getHighBits") { if ((argValues.size() == 2) && (argValues[0]) && (argValues[1])) { uint64 val = argValues[0].mUInt64; int bitCount = (int)argValues[1].GetInt64(); DbgTypedValue result; result.mType = argValues[0].mType; result.mInt64 = val >> (argValues[0].mType->GetByteCount() * 8 - bitCount); return result; } } else if (methodName == "__clearHighBits") { if ((argValues.size() == 2) && (argValues[0]) && (argValues[1])) { uint64 val = argValues[0].mUInt64; int bitCount = (int)argValues[1].GetInt64(); if (bitCount <= 0) return argValues[0]; int64 andBits = (0x8000000000000000LL) >> ((argValues[0].mType->GetByteCount() - 8) * 8 + bitCount - 1); DbgTypedValue result; result.mType = argValues[0].mType; result.mInt64 = val & ~andBits; return result; } } 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(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& args, BfSizedArray* 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(target)) { //GetAutoComplete()->CheckMemberReference(memberRefExpression->mTarget, memberRefExpression->mDotToken, memberRefExpression->mMemberName); if (memberRefExpression->mMemberName == NULL) return; if (memberRefExpression->IsA()) bypassVirtual = true; methodNodeSrc = memberRefExpression->mMemberName; if (auto attrIdentifier = BfNodeDynCast(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(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(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(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 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(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(target)) { allowImplicitThis = true; targetFunctionName = target->ToString(); } else if (auto invocationExpr = BfNodeDynCast(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* 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) { BF_ASSERT(!HasPropResult()); SetAndRestoreValue prevType(mExpectingType, wantType); SetAndRestoreValue prevResult(mResult, DbgTypedValue()); if ((mExpressionFlags & DwEvalExpressionFlag_AllowCalls) != 0) { BF_ASSERT(mCallResults != NULL); } if (mExplicitThis) mExplicitThis = FixThis(mExplicitThis); mExpectingType = wantType; VisitChildNoRef(expr); GetResult(); while (true) { if (!mResult) break; if (//(mResult.mType->mTypeCode == DbgType_Const) || (mResult.mType->mTypeCode == DbgType_Volatile)) mResult.mType = mResult.mType->mTypeParam; else break; } if ((mResult) && (wantType != NULL)) mResult = Cast(expr, mResult, wantType); if ((mReferenceId != NULL) && (mIsComplexExpression)) mReferenceId->clear(); return mResult; } BfAstNode* DbgExprEvaluator::FinalizeExplicitThisReferences(BfAstNode* headNode) { //return headNode; if (mExplicitThisExpr == NULL) return headNode; auto explicitThisExpr = mExplicitThisExpr; mExplicitThisExpr = NULL; BfReducer bfReducer; BfPrinter bfPrinter(explicitThisExpr->GetSourceData()->mRootNode, NULL, NULL); bfPrinter.mIgnoreTrivia = true; bfPrinter.mReformatting = true; bfPrinter.VisitChild(explicitThisExpr); bool doWrap = false; String thisExprStr = bfPrinter.mOutString; if (auto unaryOpExpr = BfNodeDynCast(explicitThisExpr)) { if ((unaryOpExpr->mOp != BfUnaryOp_AddressOf) && (unaryOpExpr->mOp != BfUnaryOp_Dereference)) doWrap = true; } if ((explicitThisExpr->IsA()) || (explicitThisExpr->IsA()) || (explicitThisExpr->IsA())) 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(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(); BfAstNode* newNode = NULL; if (castType != NULL) { bfReducer.ReplaceNode(replaceNode, thisExprNode); newNode = thisExprNode; } else if (auto thisNode = BfNodeDynCast(replaceNode)) { bfReducer.ReplaceNode(replaceNode, thisExprNode); newNode = thisExprNode; } else if (auto identifierNode = BfNodeDynCast(replaceNode)) { auto memberRefNode = source->mAlloc.Alloc(); bfReducer.ReplaceNode(replaceNode, memberRefNode); memberRefNode->mMemberName = identifierNode; auto tokeNode = source->mAlloc.Alloc(); 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; }