public async Task <EvaluationResult> EvaluateExpressionAsync() { VsExpression vsExpression = await _vsExpressionCreator.CreateAsync(_text, EvaluateSizeSpecifierExpressionAsync); IDebugProperty2 result; if (vsExpression.Value.StartsWith(".")) { EvaluateCommand(vsExpression.Value, out result); return(EvaluationResult.FromResult(result)); } RemoteValue remoteValue = await CreateValueFromExpressionAsync(vsExpression.Value); if (remoteValue == null) { return(EvaluationResult.Fail()); } string displayName = vsExpression.ToString(); IVariableInformation varInfo = _varInfoBuilder.Create(remoteValue, displayName, vsExpression.FormatSpecifier); result = _createPropertyDelegate.Invoke(varInfo); return(EvaluationResult.FromResult(result)); }
/// <summary> /// Tries to resolve the given expression assuming it is a member of the given variable. If /// the expression is not a member of the given variable, null is returned. Otherwise, a /// variable resulting from evaluating the expression is returned. /// </summary> IVariableInformation GetValueForMemberAccessExpression(IVariableInformation variable, VsExpression vsExpression, string displayName) { if (!vsExpression.Value.StartsWith("[")) { vsExpression = vsExpression.Clone((variable.IsPointer ? "->" : ".") + vsExpression.Value); } if (!NatvisTextMatcher.IsExpressionPath(vsExpression.Value)) { return(null); } var value = variable.GetValueForExpressionPath(vsExpression); if (value == null || value.Error) { return(null); } if (displayName != null) { return(new NamedVariableInformation(value, displayName)); } return(value); }
/// <summary> /// Evaluates an LLDB expression. It decides which expression evaluation method to use /// (e.g. LLDB, lldb-eval, path expression, etc.) depending on the Stadia SDK settings and /// the input |expression|. It doesn't support format specifiers, only expressions that /// can be directly evaluated in the LLDB environment. /// </summary> /// <param name="expression">The expression to be evaluated.</param> /// <param name="variable">The evaluation context.</param> /// <param name="natvisScope">The Natvis tokens to be resolved before evaluation.</param> /// <param name="displayName">The display name given to the result. If null the underlying /// debugger's context specific name is used.</param> /// <returns>The expression result.</returns> async Task <IVariableInformation> EvaluateLldbExpressionAsync(VsExpression expression, IVariableInformation variable, NatvisScope natvisScope, string displayName) { ExpressionEvaluationStrategy strategy = _extensionOptions.ExpressionEvaluationStrategy; var stepsRecorder = new ExpressionEvaluationRecorder.StepsRecorder(_timeSource); long startTimestampUs = _timeSource.GetTimestampUs(); IVariableInformation variableInformation = await EvaluateLldbExpressionWithMetricsAsync( expression, variable, natvisScope, displayName, strategy, stepsRecorder); // Evaluating a context variable will just return the reference to it. Because of // deferred evaluation of display values, some values could be incorrectly displayed // (in the case a context variable was changed in between two expression evaluations). // In order to prevent this, we create a copy of result if the expression was simply // a context variable. if (natvisScope.IsContextVariable(expression.Value)) { variableInformation = variableInformation.Clone(expression.FormatSpecifier); } long endTimestampUs = _timeSource.GetTimestampUs(); _expressionEvaluationRecorder.Record(strategy, ExpressionEvaluationContext.VALUE, stepsRecorder, startTimestampUs, endTimestampUs, variable.Id); return(variableInformation); }
/// <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)); }
public async Task <IVariableInformation> EvaluateExpressionAsync(string displayName, VsExpression vsExpression) { RemoteValue resultValue = await _remoteValue.EvaluateExpressionAsync(vsExpression.Value); return(resultValue == null ? null : _varInfoBuilder.Create(resultValue, displayName, formatSpecifier: vsExpression.FormatSpecifier)); }
public async Task <IVariableInformation> EvaluateExpressionLldbEvalAsync( string displayName, VsExpression vsExpression, IDictionary <string, RemoteValue> contextVariables = null) { RemoteValue resultValue = await _remoteValue.EvaluateExpressionLldbEvalAsync( vsExpression.Value, contextVariables); return(resultValue == null ? null : _varInfoBuilder.Create(resultValue, displayName, formatSpecifier: vsExpression.FormatSpecifier)); }
public IVariableInformation GetValueForExpressionPath(VsExpression vsExpression) { RemoteValue expressionValue = _remoteValue.GetValueForExpressionPath(vsExpression.Value); if (expressionValue == null) { return(null); } return(_varInfoBuilder.Create(expressionValue, formatSpecifier: vsExpression.FormatSpecifier)); }
public async Task <IVariableInformation> CreateValueFromExpressionAsync(string displayName, VsExpression vsExpression) { RemoteValue expressionValue = await _remoteValue.CreateValueFromExpressionAsync(displayName, vsExpression.Value); if (expressionValue == null) { return(null); } return(_varInfoBuilder.Create(expressionValue, formatSpecifier: vsExpression.FormatSpecifier)); }
/// <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); }
async Task <IVariableInformation> EvaluateLldbExpressionWithMetricsAsync( VsExpression expression, IVariableInformation variable, NatvisScope natvisScope, string displayName, ExpressionEvaluationStrategy strategy, ExpressionEvaluationRecorder.StepsRecorder stepsRecorder) { bool variableReplaced = false; expression = expression.MapValue( v => ReplaceScopedNames(v, natvisScope?.ScopedNames, out variableReplaced)); var lldbErrors = new List <string>(); // A helper lambda function to construct an exception given the list of lldb errors. Func <IList <string>, ExpressionEvaluationFailed> createExpressionEvaluationException = errors => { var exceptionMsg = $"Failed to evaluate expression, display name: {displayName}, " + $"expression: {expression}"; errors = errors.Where(error => !string.IsNullOrEmpty(error)).ToList(); if (errors.Any()) { exceptionMsg += $", info: {{{string.Join("; ", errors)}}}"; } return(new ExpressionEvaluationFailed(exceptionMsg)); }; if (strategy == ExpressionEvaluationStrategy.LLDB_EVAL || strategy == ExpressionEvaluationStrategy.LLDB_EVAL_WITH_FALLBACK) { IVariableInformation value; LldbEvalErrorCode errorCode; using (var step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB_EVAL)) { value = await variable.EvaluateExpressionLldbEvalAsync( displayName, expression, natvisScope.ContextVariables); errorCode = (LldbEvalErrorCode)Enum.ToObject(typeof(LldbEvalErrorCode), value.ErrorCode); step.Finalize(errorCode); } if (errorCode == LldbEvalErrorCode.Ok) { value.FallbackValueFormat = variable.FallbackValueFormat; return(value); } lldbErrors.Add(value?.ErrorMessage); if (errorCode == LldbEvalErrorCode.InvalidNumericLiteral || errorCode == LldbEvalErrorCode.InvalidOperandType || errorCode == LldbEvalErrorCode.UndeclaredIdentifier) { // In the case of a well-known error, there's no need to fallback to // LLDB, as it will fail with the same error. throw createExpressionEvaluationException(lldbErrors); } } if (strategy == ExpressionEvaluationStrategy.LLDB) { // If lldb-eval is not enabled, try to interpret the expression as member access // before using LLDB to evaluate the expression in the context of the variable. IVariableInformation value; LLDBErrorCode errorCode; using (var step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB_VARIABLE_PATH)) { value = GetValueForMemberAccessExpression(variable, expression, displayName); errorCode = value != null && !value.Error ? LLDBErrorCode.OK : LLDBErrorCode.ERROR; step.Finalize(errorCode); } if (errorCode == LLDBErrorCode.OK) { value.FallbackValueFormat = variable.FallbackValueFormat; return(value); } lldbErrors.Add(value?.ErrorMessage); } if (strategy == ExpressionEvaluationStrategy.LLDB || strategy == ExpressionEvaluationStrategy.LLDB_EVAL_WITH_FALLBACK) { IVariableInformation value; LLDBErrorCode errorCode; using (var step = stepsRecorder.NewStep(ExpressionEvaluationEngine.LLDB)) { value = await EvaluateExpressionInVariableScopeAsync( variable, expression, displayName); errorCode = value != null && !value.Error ? LLDBErrorCode.OK : LLDBErrorCode.ERROR; step.Finalize(errorCode); } if (errorCode == LLDBErrorCode.OK) { value.FallbackValueFormat = variable.FallbackValueFormat; return(value); } lldbErrors.Add(value?.ErrorMessage); } throw createExpressionEvaluationException(lldbErrors); }