protected override void HandleEvaluateRequestAsync()

in src/OpenDebugAD7/AD7DebugSession.cs [2830:2985]


        protected override void HandleEvaluateRequestAsync(IRequestResponder<EvaluateArguments, EvaluateResponse> responder)
        {
            EvaluateArguments.ContextValue context = responder.Arguments.Context.GetValueOrDefault(EvaluateArguments.ContextValue.Unknown);
            int frameId = responder.Arguments.FrameId.GetValueOrDefault(-1);
            string expression = responder.Arguments.Expression;

            if (expression == null)
            {
                responder.SetError(new ProtocolException("Failed to handle EvaluateRequest: Missing 'expression'"));
                return;
            }

            // if we are not stopped, return evaluation failure
            if (!m_isStopped)
            {
                responder.SetError(new ProtocolException("Failed to handle EvaluateRequest", new Message(1105, AD7Resources.Error_TargetNotStopped)));
                return;
            }
            DateTime evaluationStartTime = DateTime.Now;

            bool isExecInConsole = false;
            // If the expression isn't empty and its a Repl request, do additional checking
            if (!String.IsNullOrEmpty(expression) && context == EvaluateArguments.ContextValue.Repl)
            {
                // If this is an -exec command (or starts with '`') treat it as a console command and log telemetry
                if (expression.StartsWith("-exec", StringComparison.Ordinal) || expression[0] == '`')
                    isExecInConsole = true;
            }

            int hr;
            ErrorBuilder eb = new ErrorBuilder(() => AD7Resources.Error_Scenario_Evaluate);
            IDebugStackFrame2 frame;

            bool success = false;
            if (frameId == -1 && isExecInConsole)
            {
                // If exec in console and no stack frame, evaluate off the top frame.
                success = m_frameHandles.TryGetFirst(out frame);
            }
            else
            {
                success = m_frameHandles.TryGet(frameId, out frame);
            }

            if (!success)
            {
                Dictionary<string, object> properties = new Dictionary<string, object>();
                properties.Add(DebuggerTelemetry.TelemetryStackFrameId, frameId);
                properties.Add(DebuggerTelemetry.TelemetryExecuteInConsole, isExecInConsole);
                DebuggerTelemetry.ReportError(DebuggerTelemetry.TelemetryEvaluateEventName, 1108, "Invalid frameId", properties);
                responder.SetError(new ProtocolException("Cannot evaluate expression on the specified stack frame."));
                return;
            }

            uint radix = Constants.EvaluationRadix;

            if (responder.Arguments.Format != null)
            {
                ValueFormat format = responder.Arguments.Format;

                if (format.Hex == true)
                {
                    radix = 16;
                }
            }

            if (m_settingsCallback != null)
            {
                // MIEngine generally gets the radix from IDebugSettingsCallback110 rather than using the radix passed
                m_settingsCallback.Radix = radix;
            }

            IDebugExpressionContext2 expressionContext;
            hr = frame.GetExpressionContext(out expressionContext);
            eb.CheckHR(hr);

            IDebugExpression2 expressionObject;
            string error;
            uint errorIndex;
            hr = expressionContext.ParseText(expression, enum_PARSEFLAGS.PARSE_EXPRESSION, Constants.ParseRadix, out expressionObject, out error, out errorIndex);
            if (!string.IsNullOrEmpty(error))
            {
                // TODO: Is this how errors should be returned?
                DebuggerTelemetry.ReportError(DebuggerTelemetry.TelemetryEvaluateEventName, 4001, "Error parsing expression");
                responder.SetError(new ProtocolException(error));
                return;
            }
            eb.CheckHR(hr);
            eb.CheckOutput(expressionObject);

            // NOTE: This is the same as what vssdebug normally passes for the watch window
            enum_EVALFLAGS flags = enum_EVALFLAGS.EVAL_RETURNVALUE |
                enum_EVALFLAGS.EVAL_NOEVENTS |
                (enum_EVALFLAGS)enum_EVALFLAGS110.EVAL110_FORCE_REAL_FUNCEVAL;

            if (context == EvaluateArguments.ContextValue.Hover) // No side effects for data tips
            {
                flags |= enum_EVALFLAGS.EVAL_NOSIDEEFFECTS;
            }

            IDebugProperty2 property;
            if (expressionObject is IDebugExpressionDAP expressionDapObject)
            {
                DAPEvalFlags dapEvalFlags = DAPEvalFlags.NONE;
                if (context == EvaluateArguments.ContextValue.Clipboard)
                {
                    dapEvalFlags |= DAPEvalFlags.CLIPBOARD_CONTEXT;
                }
                hr = expressionDapObject.EvaluateSync(flags, dapEvalFlags, Constants.EvaluationTimeout, null, out property);
            }
            else
            {
                hr = expressionObject.EvaluateSync(flags, Constants.EvaluationTimeout, null, out property);
            }

            eb.CheckHR(hr);
            eb.CheckOutput(property);

            DEBUG_PROPERTY_INFO[] propertyInfo = new DEBUG_PROPERTY_INFO[1];
            enum_DEBUGPROP_INFO_FLAGS propertyInfoFlags = GetDefaultPropertyInfoFlags();

            if (context == EvaluateArguments.ContextValue.Hover) // No side effects for data tips
            {
                propertyInfoFlags |= (enum_DEBUGPROP_INFO_FLAGS)enum_DEBUGPROP_INFO_FLAGS110.DEBUGPROP110_INFO_NOSIDEEFFECTS;
            }

            property.GetPropertyInfo(propertyInfoFlags, radix, Constants.EvaluationTimeout, null, 0, propertyInfo);

            // If the expression evaluation produces an error result and we are trying to get the expression for data tips
            // return a failure result so that VS code won't display the error message in data tips
            if (((propertyInfo[0].dwAttrib & enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_VALUE_ERROR) == enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_VALUE_ERROR) && context == EvaluateArguments.ContextValue.Hover)
            {
                responder.SetError(new ProtocolException("Evaluation error"));
                return;
            }

            string memoryReference = AD7Utils.GetMemoryReferenceFromIDebugProperty(property);

            Variable variable = m_variableManager.CreateVariable(ref propertyInfo[0], propertyInfoFlags, memoryReference);

            if (context != EvaluateArguments.ContextValue.Hover)
            {
                DebuggerTelemetry.ReportEvaluation(
                    ((propertyInfo[0].dwAttrib & enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_VALUE_ERROR) == enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_VALUE_ERROR),
                    DateTime.Now - evaluationStartTime,
                    isExecInConsole ? new Dictionary<string, object>() { { DebuggerTelemetry.TelemetryExecuteInConsole, true } } : null);
            }

            responder.SetResponse(new EvaluateResponse()
            {
                Result = variable.Value,
                Type = variable.Type,
                VariablesReference = variable.VariablesReference,
                MemoryReference = memoryReference
            });
        }