File: FunctionResolver.cs
Web Access
Project: src\src\ExpressionEvaluator\Core\Source\FunctionResolver\Microsoft.CodeAnalysis.FunctionResolver.csproj (Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Debugging;
using Microsoft.VisualStudio.Debugger;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.ComponentInterfaces;
using Microsoft.VisualStudio.Debugger.FunctionResolution;
using Microsoft.VisualStudio.Debugger.Symbols;
 
namespace Microsoft.CodeAnalysis.ExpressionEvaluator
{
    internal abstract class FunctionResolver :
        FunctionResolverBase<DkmProcess, DkmClrModuleInstance, DkmRuntimeFunctionResolutionRequest>,
        IDkmRuntimeFunctionResolver,
        IDkmMetaDataPointerInvalidatedNotification,
        IDkmModuleInstanceLoadNotification,
        IDkmModuleInstanceUnloadNotification,
        IDkmModuleModifiedNotification,
        IDkmModuleSymbolsLoadedNotification
    {
        void IDkmRuntimeFunctionResolver.EnableResolution(DkmRuntimeFunctionResolutionRequest request, DkmWorkList workList)
        {
            if (request.LineOffset > 0)
            {
                return;
            }
 
            EnableResolution(request.Process, request, OnFunctionResolved(workList));
        }
 
        void IDkmModuleInstanceLoadNotification.OnModuleInstanceLoad(DkmModuleInstance moduleInstance, DkmWorkList workList, DkmEventDescriptorS eventDescriptor)
        {
            OnModuleLoad(moduleInstance, workList);
        }
 
        void IDkmModuleInstanceUnloadNotification.OnModuleInstanceUnload(DkmModuleInstance moduleInstance, DkmWorkList workList, DkmEventDescriptor eventDescriptor)
        {
            // Implementing IDkmModuleInstanceUnloadNotification
            // (with Synchronized="true" in .vsdconfigxml) prevents
            // caller from unloading modules while binding.
        }
 
        void IDkmModuleModifiedNotification.OnModuleModified(DkmModuleInstance moduleInstance)
        {
            // Implementing IDkmModuleModifiedNotification
            // (with Synchronized="true" in .vsdconfigxml) prevents
            // caller from modifying modules while binding.
        }
 
        void IDkmMetaDataPointerInvalidatedNotification.OnMetaDataPointerInvalidated(DkmClrModuleInstance moduleInstance)
        {
            // Implementing IDkmMetaDataPointerInvalidatedNotification
            // (with Synchronized="true" in .vsdconfigxml) prevents
            // caller from modifying modules while binding.
        }
 
        void IDkmModuleSymbolsLoadedNotification.OnModuleSymbolsLoaded(DkmModuleInstance moduleInstance, DkmModule module, bool isReload, DkmWorkList workList, DkmEventDescriptor eventDescriptor)
        {
            OnModuleLoad(moduleInstance, workList);
        }
 
        private void OnModuleLoad(DkmModuleInstance moduleInstance, DkmWorkList workList)
        {
            var module = moduleInstance as DkmClrModuleInstance;
            Debug.Assert(module != null); // <Filter><RuntimeId RequiredValue="DkmRuntimeId.Clr"/></Filter> should ensure this.
            if (module == null)
            {
                // Only interested in managed modules.
                return;
            }
 
            OnModuleLoad(module.Process, module, OnFunctionResolved(workList));
        }
 
        internal sealed override bool ShouldEnableFunctionResolver(DkmProcess process)
        {
            var dataItem = process.GetDataItem<FunctionResolverDataItem>();
            if (dataItem == null)
            {
                var enable = ShouldEnable(process);
                dataItem = new FunctionResolverDataItem(enable);
                process.SetDataItem(DkmDataCreationDisposition.CreateNew, dataItem);
            }
            return dataItem.Enabled;
        }
 
        internal sealed override IEnumerable<DkmClrModuleInstance> GetAllModules(DkmProcess process)
        {
            foreach (var runtimeInstance in process.GetRuntimeInstances())
            {
                var runtime = runtimeInstance as DkmClrRuntimeInstance;
                if (runtime == null)
                {
                    continue;
                }
                foreach (var moduleInstance in runtime.GetModuleInstances())
                {
                    // Only interested in managed modules.
                    if (moduleInstance is DkmClrModuleInstance module)
                    {
                        yield return module;
                    }
                }
            }
        }
 
        internal sealed override string GetModuleName(DkmClrModuleInstance module)
        {
            return module.Name;
        }
 
        internal sealed override unsafe bool TryGetMetadata(DkmClrModuleInstance module, out byte* pointer, out int length)
        {
            try
            {
                pointer = (byte*)module.GetMetaDataBytesPtr(out var size);
                length = (int)size;
                return true;
            }
            catch (Exception e) when (DkmExceptionUtilities.IsBadOrMissingMetadataException(e))
            {
                pointer = null;
                length = 0;
                return false;
            }
        }
 
        internal sealed override DkmRuntimeFunctionResolutionRequest[] GetRequests(DkmProcess process)
        {
            return process.GetRuntimeFunctionResolutionRequests();
        }
 
        internal sealed override string GetRequestModuleName(DkmRuntimeFunctionResolutionRequest request)
        {
            return request.ModuleName;
        }
 
        internal sealed override Guid GetLanguageId(DkmRuntimeFunctionResolutionRequest request)
        {
            return request.CompilerId.LanguageId;
        }
 
        private static OnFunctionResolvedDelegate<DkmClrModuleInstance, DkmRuntimeFunctionResolutionRequest> OnFunctionResolved(DkmWorkList workList)
        {
            return (DkmClrModuleInstance module,
                        DkmRuntimeFunctionResolutionRequest request,
                        int token,
                        int version,
                        int ilOffset) =>
            {
                var address = DkmClrInstructionAddress.Create(
                    module.RuntimeInstance,
                    module,
                    new DkmClrMethodId(Token: token, Version: (uint)version),
                    NativeOffset: uint.MaxValue,
                    ILOffset: (uint)ilOffset,
                    CPUInstruction: null);
                // Use async overload of OnFunctionResolved to avoid deadlock.
                request.OnFunctionResolved(workList, address, result => { });
            };
        }
 
        private static readonly Guid s_messageSourceId = new Guid("ac353c9b-c599-427b-9424-cbe1ad19f81e");
 
        private static bool ShouldEnable(DkmProcess process)
        {
            var message = DkmCustomMessage.Create(
                process.Connection,
                process,
                s_messageSourceId,
                MessageCode: 1, // Is legacy EE enabled?
                Parameter1: null,
                Parameter2: null);
            try
            {
                var reply = message.SendLower();
                var result = (int)reply.Parameter1;
                // Possible values are 0 = false, 1 = true, 2 = not ready.
                // At this point, we should only get 0 or 1, but to be
                // safe, treat values other than 0 or 1 as false.
                Debug.Assert(result == 0 || result == 1);
                return result == 0;
            }
            catch (NotImplementedException)
            {
                return false;
            }
        }
 
        private sealed class FunctionResolverDataItem : DkmDataItem
        {
            internal FunctionResolverDataItem(bool enabled)
            {
                Enabled = enabled;
            }
 
            internal readonly bool Enabled;
        }
    }
}