/// <summary> /// Try to evaluate the given expression in the context of the variable. /// Imagine the variable is a class and the expression is in a method of that class. /// Member variables can be accessed and take precedence over global variables of the same name. /// Globals can be accessed as well. Returns a variable representing the result of the evaluation /// or null if the expression can't be evaluated. /// </summary> Task <IVariableInformation> EvaluateExpressionInVariableScopeAsync( IVariableInformation variable, VsExpression vsExpression, string displayName) { if (variable.IsPointer || variable.IsReference) { variable = variable.Dereference(); } return(variable.EvaluateExpressionAsync(displayName, vsExpression)); }
/// <summary> /// Declare a variable in using the given variable scope to execute the value expression. /// Token replacement using scopedNames is done against both the variable name and the /// value expression. /// </summary> /// <exception cref="ExpressionEvaluationFailed"> /// Expression to declare the variable failed to evaluate. /// </exception> public async Task DeclareVariableAsync(IVariableInformation variable, string variableName, string valueExpression, NatvisScope natvisScope) { string scratchVar = ReplaceScopedNames(variableName, natvisScope?.ScopedNames, out bool ignore); VsExpression vsExpression = _vsExpressionCreator.Create(valueExpression, "") .MapValue(e => ReplaceScopedNames(e, natvisScope?.ScopedNames, out ignore)); // Declare variable and return it. Pure declaration expressions will always return // error because these expressions don't return a valid value. VsExpression createExpression = vsExpression.MapValue(e => $"auto {scratchVar}={e}; {scratchVar}"); if (variable.IsPointer || variable.IsReference) { variable = variable.Dereference(); if (variable == null) { string failMsg = $"Failed to dereference pointer: Name: {variableName}"; _logger.Error(failMsg); throw new ExpressionEvaluationFailed(failMsg); } } // TODO: Split the logic for LLDB and lldb-eval. Currently, LLDB is always // used to create a scratch variable (even if lldb-eval is the selected engine). IVariableInformation result = await variable.EvaluateExpressionAsync(variableName, createExpression); if (result != null && !result.Error && natvisScope != null) { // Result of 'auto {scratchVar}={e}; {scratchVar}' creates a copy of the scratch // variable. Evaluating '{scratchVar}' returns the reference to the original // variable. By using the original variable we make sure that the we always use its // up-to-date value. // TODO: Use RemoteFrame.FindValue to get the scratch variable. // EvaluateExpression method already is optimised for the case of fetching scratch // variables, but it isn't a convenient one. result = await variable.EvaluateExpressionAsync( variableName, _vsExpressionCreator.Create($"{scratchVar}", "")); if (result != null && !result.Error) { natvisScope.AddContextVariable(scratchVar, result.GetRemoteValue()); return; } } string msg = $"Failed to declare variable: Name: {variableName}, " + $"Expression: {valueExpression}"; string resultMessage = result?.ErrorMessage; if (!string.IsNullOrEmpty(resultMessage)) { msg += $", Info: {{{resultMessage}}}"; } _logger.Error(msg); throw new ExpressionEvaluationFailed(msg); }