/// <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);
        }
Esempio n. 2
0
 protected LeafEntity(IVariableInformation variable, NatvisDiagnosticLogger logger,
                      NatvisExpressionEvaluator evaluator, NatvisScope natvisScope)
 {
     _variable    = variable;
     _logger      = logger;
     _evaluator   = evaluator;
     _natvisScope = natvisScope;
 }
Esempio n. 3
0
 ExpandedItemEntity(IVariableInformation variable, NatvisScope natvisScope,
                    ExpandedItemType expandedItem, NatvisDiagnosticLogger logger,
                    NatvisEntityStore store, NatvisExpressionEvaluator evaluator)
     : base(variable, logger, evaluator, natvisScope)
 {
     _expandedItem = expandedItem;
     _store        = store;
 }
Esempio n. 4
0
 TreeItemsEntity(IVariableInformation variable, NatvisScope natvisScope,
                 TreeItemsType treeItems, NatvisDiagnosticLogger logger,
                 NatvisEntityStore store, NatvisExpressionEvaluator evaluator,
                 NatvisSizeParser sizeParser)
     : base(variable, logger, evaluator, natvisScope)
 {
     _treeItems  = treeItems;
     _store      = store;
     _sizeParser = sizeParser;
 }
Esempio n. 5
0
 public VisualizerInfo(VisualizerType viz, TypeName name)
 {
     Visualizer = viz;
     // add the template parameter macro values
     NatvisScope = new NatvisScope();
     for (int i = 0; i < name.Args.Count; ++i)
     {
         NatvisScope.AddScopedName($"$T{i + 1}", name.Args[i].FullyQualifiedName);
     }
 }
 SmartPointerEntity(NatvisExpressionEvaluator evaluator, NatvisDiagnosticLogger logger,
                    IVariableInformation variable, SmartPointerType smartPointerItem,
                    NatvisScope natvisScope, IChildAdapter fallbackAdapter)
 {
     _evaluator        = evaluator;
     _logger           = logger;
     _variable         = variable;
     _smartPointerItem = smartPointerItem;
     _natvisScope      = natvisScope;
     _fallbackAdapter  = fallbackAdapter;
 }
 CustomListItemsEntity(IVariableInformation variable, NatvisScope natvisScope,
                       CustomListItemsType customList, NatvisDiagnosticLogger logger,
                       NatvisEntityStore store, NatvisExpressionEvaluator evaluator,
                       IVariableNameTransformer nameTransformer, CodeBlockParser parser)
     : base(variable, logger, evaluator, natvisScope)
 {
     _customList      = customList;
     _store           = store;
     _nameTransformer = nameTransformer;
     _parser          = parser;
 }
Esempio n. 8
0
 SyntheticItemEntity(IVariableInformation variable, NatvisScope natvisScope,
                     SyntheticItemType item, NatvisDiagnosticLogger logger,
                     NatvisEntityStore store, NatvisExpressionEvaluator evaluator,
                     NatvisStringFormatter stringFormatter,
                     NatvisCollectionEntity.Factory natvisCollectionFactory)
     : base(variable, logger, evaluator, natvisScope)
 {
     _item                    = item;
     _store                   = store;
     _stringFormatter         = stringFormatter;
     _natvisCollectionFactory = natvisCollectionFactory;
 }
        /// <summary>
        /// Asynchronously processes a mixed format string that contains literal text and
        /// expressions embedded inside curly braces. The expressions are evaluated within the
        /// context of the variable specified, and subsequently formatted using the subexpression
        /// formatter provided.
        /// </summary>
        /// <remarks>
        /// Examples:
        ///   FormatValueAsync("Some literal text and an {expression}", varInfo, natvisScope,
        ///   subexpressionFormatter);
        ///   FormatValueAsync("{{Escaped, literal text.}}", varInfo, natvisScope,
        ///   subexpressionFormatter);
        /// </remarks>
        async Task <string> FormatValueAsync(
            string format, IVariableInformation variable, NatvisScope natvisScope,
            Func <IVariableInformation, Task <string> > subexpressionFormatter)
        {
            if (string.IsNullOrWhiteSpace(format))
            {
                return(string.Empty);
            }

            var value = new StringBuilder();

            for (int i = 0; i < format.Length; ++i)
            {
                if (format[i] == '{')
                {
                    if (i + 1 < format.Length && format[i + 1] == '{')
                    {
                        value.Append('{');
                        i++;
                        continue;
                    }

                    // start of expression
                    Match m = _expressionRegex.Match(format.Substring(i));
                    if (m.Success)
                    {
                        string expression = format.Substring(i + 1, m.Length - 2);
                        IVariableInformation exprValue = await _evaluator.EvaluateExpressionAsync(
                            expression, variable, natvisScope, null);

                        value.Append(await subexpressionFormatter(exprValue));
                        i += m.Length - 1;
                    }
                }
                else if (format[i] == '}')
                {
                    // Accept both } and }} as closing braces to match native behavior.
                    value.Append('}');
                    if (i + 1 < format.Length && format[i + 1] == '}')
                    {
                        i++;
                    }
                }
                else
                {
                    value.Append(format[i]);
                }
            }

            return(value.ToString());
        }
        /// <summary>
        /// Evaluates a condition in the context of a variable.
        /// </summary>
        /// <returns>The result of the condition evaluation.</returns>
        public async Task <bool> EvaluateConditionAsync(string condition,
                                                        IVariableInformation variable,
                                                        NatvisScope natvisScope)
        {
            if (string.IsNullOrWhiteSpace(condition))
            {
                return(true);
            }

            IVariableInformation exprValue =
                await EvaluateExpressionAsync(condition, variable, natvisScope, null);

            return(exprValue.IsTruthy);
        }
 /// <summary>
 /// Invokes GetExpressionValue, but returns error variable in case of evaluation error.
 /// </summary>
 public async Task <IVariableInformation> GetExpressionValueOrErrorAsync(
     string expression, IVariableInformation variable, NatvisScope natvisScope,
     string displayName, string natvisType)
 {
     try
     {
         return(await EvaluateExpressionAsync(expression, variable, natvisScope,
                                              displayName));
     }
     catch (ExpressionEvaluationFailed e)
     {
         return(NatvisErrorUtils.LogAndGetEvaluationError(
                    _logger, natvisType, variable?.TypeName, displayName, e.Message));
     }
 }
Esempio n. 12
0
        public override async Task <IList <IVariableInformation> > GetChildrenAsync(
            int from, int count)
        {
            await InitAsync();

            var result = new List <IVariableInformation>();

            if (_store.ValidationError != null)
            {
                result.Add(_store.ValidationError);
                return(result.GetRange(from, count));
            }

            var indexDic = new NatvisScope(_natvisScope);

            for (int index = from; index < from + count; index++)
            {
                IVariableInformation varInfo = await _store.GetOrEvaluateAsync(index, async i => {
                    indexDic.AddScopedName("$i", $"{i}U");
                    string displayName = $"[{i}]";

                    // From the list of all <ValueNode> children, filter all with non-empty body
                    // and return the first which Condition evaluates to true.
                    IndexNodeType valueNode =
                        await _indexListItems
                        .ValueNode?.Where(v => !string.IsNullOrWhiteSpace(v.Value))
                        .FirstOrDefaultAsync(v => _evaluator.EvaluateConditionAsync(
                                                 v.Condition, _variable, indexDic));

                    if (valueNode == null)
                    {
                        // For the current index $i, there is no <ValueNode> which passes the
                        // Condition check.
                        return(new ErrorVariableInformation(
                                   displayName, "<Error> No valid <ValueNode> found."));
                    }

                    return(await _evaluator.GetExpressionValueOrErrorAsync(
                               valueNode.Value, _variable, indexDic, displayName, "IndexListItems"));
                });

                result.Add(varInfo);
            }

            return(result);
        }
Esempio n. 13
0
        internal NatvisSyntheticVariableInformation(
            NatvisStringFormatter stringFormatter,
            NatvisCollectionEntity.Factory natvisCollectionFactory, NatvisScope natvisScope,
            SyntheticItemType syntheticItemType, IVariableInformation varInfo, string displayValue)
            : base(varInfo)
        {
            _stringFormatter         = stringFormatter;
            _natvisCollectionFactory = natvisCollectionFactory;
            _natvisScope             = natvisScope;
            _syntheticItemType       = syntheticItemType;
            _displayValue            = displayValue;

            // Synthetic items should never show the raw view.
            if (syntheticItemType.Expand != null)
            {
                syntheticItemType.Expand.HideRawView = true;
            }
        }
        /// <summary>
        /// Evaluates a Natvis expression in the context of a variable asynchronously.
        ///
        /// Examples:
        ///   "myData[0] == true"
        ///   "myData[$i]"
        ///   "MyContainer<$T1, $T2>",
        ///   "(char*)myData,[myLength]s"
        /// </summary>
        /// <param name="expression">The expression to evaluate. Natvis tokens are resolved prior to
        /// evaluation, ex. $i, $Tx. </param>
        /// <param name="variable"></param>
        /// <param name="scopedNames">The Natvis tokens to resolve in expression.</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>
        public async Task <IVariableInformation> EvaluateExpressionAsync(
            string expression, IVariableInformation variable, NatvisScope natvisScope,
            string displayName)
        {
            var vsExpression = await _vsExpressionCreator.CreateAsync(
                expression, async (sizeExpression) =>
            {
                IVariableInformation value = await EvaluateLldbExpressionAsync(
                    _vsExpressionCreator.Create(sizeExpression, ""), variable, natvisScope,
                    displayName);
                uint size;
                if (!uint.TryParse(await value.ValueAsync(), out size))
                {
                    throw new ExpressionEvaluationFailed("Expression isn't a uint");
                }

                return(size);
            });

            return(await EvaluateLldbExpressionAsync(vsExpression, variable, natvisScope,
                                                     displayName));
        }
Esempio n. 15
0
 public INatvisEntity Create(IVariableInformation variable, NatvisScope natvisScope,
                             IndexListItemsType indexListItems) =>
 new IndexListItemsEntity(variable, natvisScope, indexListItems, _logger,
                          new NatvisEntityStore(), _evaluator, _sizeParser);
        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);
        }
 public INatvisEntity Create(IVariableInformation variable, NatvisScope natvisScope,
                             CustomListItemsType customListItems) =>
 new CustomListItemsEntity(variable, natvisScope, customListItems, _logger,
                           new NatvisEntityStore(), _evaluator, _nameTransformer,
                           new CodeBlockParser(_evaluator));
Esempio n. 18
0
        /// <summary>
        /// Processes a SizeType array and returns the first valid size value.
        ///
        /// Throws ExpressionEvaluationFailed in case of an evaluation error and
        /// InvalidOperationException if valid <Size> node is not found.
        /// </summary>
        /// <returns></returns>
        internal async Task <uint> ParseSizeAsync(SizeType[] sizes, IVariableInformation varInfo,
                                                  NatvisScope natvisScope)
        {
            if (sizes == null)
            {
                throw new InvalidOperationException("Valid <Size> node not found.");
            }

            foreach (SizeType curSize in sizes)
            {
                string errorMsg = null;
                string sizeText = null;
                try
                {
                    if (!NatvisViewsUtil.IsViewVisible(varInfo.FormatSpecifier, curSize.IncludeView,
                                                       curSize.ExcludeView) ||
                        !await _evaluator.EvaluateConditionAsync(curSize.Condition, varInfo,
                                                                 natvisScope))
                    {
                        continue;
                    }

                    if (string.IsNullOrEmpty(curSize.Value))
                    {
                        errorMsg = "The expression cannot be empty.";
                    }
                    else
                    {
                        IVariableInformation sizeVarInfo = await _evaluator.EvaluateExpressionAsync(
                            curSize.Value, varInfo, natvisScope, null);

                        sizeVarInfo.FallbackValueFormat = ValueFormat.Default;
                        sizeText = await sizeVarInfo.ValueAsync();
                    }
                }
                catch (ExpressionEvaluationFailed ex) when(curSize.Optional)
                {
                    errorMsg = ex.Message;
                }

                uint size = 0;
                if (errorMsg == null)
                {
                    if (!ParseUint(sizeText, out size))
                    {
                        errorMsg = "The expression's value was not a number. " +
                                   $"Expression='{curSize.Value}' Value='{sizeText}'";
                    }
                }

                if (errorMsg != null)
                {
                    if (!curSize.Optional)
                    {
                        throw new ExpressionEvaluationFailed("Failed to evaluate <Size> node. " +
                                                             errorMsg);
                    }

                    _logger.Verbose(() => $"Failed to evaluate <Size> node for type" +
                                    $" '{varInfo.TypeName}'. Reason: {errorMsg}");
                }
                else
                {
                    return(size);
                }
            }

            throw new InvalidOperationException("Valid <Size> node not found.");
        }
            public INatvisEntity Create(IVariableInformation variable, ExpandType expandType,
                                        NatvisScope natvisScope)
            {
                var children = new List <INatvisEntity>();

                if (expandType?.Items == null)
                {
                    return(new NatvisCollectionEntity(children, variable,
                                                      expandType?.HideRawView ?? true));
                }

                foreach (object item in expandType.Items)
                {
                    if (item is ItemType itemType)
                    {
                        children.Add(_itemFactory.Create(variable, natvisScope, itemType));
                    }
                    else if (item is SyntheticItemType syntheticItemType)
                    {
                        children.Add(_syntheticItemFactory.Create(variable, natvisScope,
                                                                  syntheticItemType, this));
                    }
                    else if (item is ExpandedItemType expandedItemType)
                    {
                        children.Add(
                            _expandedItemFactory.Create(variable, natvisScope, expandedItemType));
                    }
                    else if (item is IndexListItemsType indexListItems)
                    {
                        children.Add(RangedNatvisEntityDecorator.First(
                                         _maxChildrenPerRangeIndexListItems,
                                         _indexListItemsFactory.Create(variable, natvisScope, indexListItems)));
                    }
                    else if (item is ArrayItemsType arrayItems)
                    {
                        children.Add(RangedNatvisEntityDecorator.First(
                                         _maxChildrenPerRangeArrayItems,
                                         _arrayItemsFactory.Create(variable, natvisScope, arrayItems)));
                    }
                    else if (item is LinkedListItemsType linkedListItems)
                    {
                        children.Add(RangedNatvisEntityDecorator.First(
                                         _maxChildrenPerRangeLinkedListItems,
                                         _linkedListItemsFactory.Create(variable, natvisScope,
                                                                        linkedListItems)));
                    }
                    else if (item is TreeItemsType treeItems)
                    {
                        children.Add(RangedNatvisEntityDecorator.First(
                                         _maxChildrenPerRangeTreeItems,
                                         _treeItemsFactory.Create(variable, natvisScope, treeItems)));
                    }
                    else if (item is CustomListItemsType customListItems &&
                             _natvisExperimentsEnabled())
                    {
                        // Use "MaxItemsPerView" attribute to limit the number of items per view.
                        // If not defined (default value is 0), use the "default" limit to avoid
                        // huge lists.
                        // TODO: Consider removing the default limit if the rendering performance
                        // is no longer an issue.
                        // The default limit in Visual Studio (Code) is 5000 -- see docs for
                        // MaxItemsPerViewType at https://code.visualstudio.com/docs/cpp/natvis.
                        int maxItemsPerView = customListItems.MaxItemsPerView > 0
                                                  ? (int)customListItems.MaxItemsPerView
                                                  : _maxChildrenPerRangeCustomListItems;

                        children.Add(RangedNatvisEntityDecorator.First(
                                         maxItemsPerView, _customListItemsFactory.Create(variable, natvisScope,
                                                                                         customListItems)));
                    }
Esempio n. 20
0
 public CustomListItemsContext(NatvisScope natvisScope, IVariableInformation variable)
 {
     NatvisScope = natvisScope;
     Variable    = variable;
 }
Esempio n. 21
0
 public SmartPointerEntity Create(IVariableInformation variable,
                                  SmartPointerType smartPointerItem,
                                  NatvisScope natvisScope,
                                  IChildAdapter fallbackAdapter) =>
 new SmartPointerEntity(_evaluator, _logger, variable, smartPointerItem, natvisScope,
                        fallbackAdapter);
Esempio n. 22
0
 public INatvisEntity Create(IVariableInformation variable, NatvisScope natvisScope,
                             TreeItemsType treeItems) =>
 new TreeItemsEntity(variable, natvisScope, treeItems, _logger,
                     new NatvisEntityStore(), _evaluator, _sizeParser);
Esempio n. 23
0
 /// <summary>
 /// Constructs a copy of the other natvis scope.
 /// </summary>
 public NatvisScope(NatvisScope other)
 {
     ScopedNames      = new Dictionary <string, string>(other.ScopedNames);
     ContextVariables = new Dictionary <string, RemoteValue>(other.ContextVariables);
 }
Esempio n. 24
0
 public INatvisEntity Create(IVariableInformation variable, NatvisScope natvisScope,
                             ExpandedItemType expandedItem) =>
 new ExpandedItemEntity(variable, natvisScope, expandedItem, _logger,
                        new NatvisEntityStore(), _evaluator);
Esempio n. 25
0
 public INatvisEntity Create(IVariableInformation variable, NatvisScope natvisScope,
                             SyntheticItemType item,
                             NatvisCollectionEntity.Factory natvisCollectionFactory) =>
 new SyntheticItemEntity(variable, natvisScope, item, _logger,
                         new NatvisEntityStore(), _evaluator, _stringFormatter,
                         natvisCollectionFactory);
        /// <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);
        }