/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.CallStack; using dnSpy.Contracts.Debugger.DotNet.Disassembly; using dnSpy.Contracts.Debugger.DotNet.Evaluation; using dnSpy.Contracts.Debugger.DotNet.Mono; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Contracts.Disassembly; using dnSpy.Contracts.Metadata; using dnSpy.Debugger.DotNet.Metadata; using dnSpy.Debugger.DotNet.Mono.CallStack; using dnSpy.Debugger.DotNet.Mono.Impl.Evaluation.Hooks; using dnSpy.Debugger.DotNet.Mono.Properties; using Mono.Debugger.Soft; namespace dnSpy.Debugger.DotNet.Mono.Impl.Evaluation { sealed class DbgMonoDebugInternalRuntimeImpl : DbgMonoDebugInternalRuntime, IDbgDotNetRuntime, IMonoDebugRuntime { public override MonoDebugRuntimeKind Kind { get; } public override DmdRuntime ReflectionRuntime { get; } public override DbgRuntime Runtime { get; } public DbgDotNetDispatcher Dispatcher { get; } public DbgDotNetRuntimeFeatures Features { get; } IMonoDebugValueConverter IMonoDebugRuntime.ValueConverter => monoDebugValueConverter; readonly DbgEngineImpl engine; readonly Dictionary classHooks; readonly IMonoDebugValueConverter monoDebugValueConverter; public DbgMonoDebugInternalRuntimeImpl(DbgEngineImpl engine, DbgRuntime runtime, DmdRuntime reflectionRuntime, MonoDebugRuntimeKind monoDebugRuntimeKind) { this.engine = engine ?? throw new ArgumentNullException(nameof(engine)); Runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); ReflectionRuntime = reflectionRuntime ?? throw new ArgumentNullException(nameof(reflectionRuntime)); Kind = monoDebugRuntimeKind; Dispatcher = new DbgDotNetDispatcherImpl(engine); Features = CalculateFeatures(engine.MonoVirtualMachine); reflectionRuntime.GetOrCreateData(() => runtime); monoDebugValueConverter = new MonoDebugValueConverterImpl(this); classHooks = new Dictionary(); foreach (var info in ClassHookProvider.Create(engine, this)) { Debug2.Assert(info.Hook is not null); Debug.Assert(!classHooks.ContainsKey(info.WellKnownType)); classHooks.Add(info.WellKnownType, info.Hook); } } static DbgDotNetRuntimeFeatures CalculateFeatures(VirtualMachine vm) { var res = DbgDotNetRuntimeFeatures.ObjectIds | DbgDotNetRuntimeFeatures.NoDereferencePointers; if (!vm.Version.AtLeast(2, 24)) res |= DbgDotNetRuntimeFeatures.NoGenericMethods; // We need FuncEvalOptions.ReturnOutThis support so func-eval of Task/ObjectIdForDebugger // prop updates the struct's task field if (!vm.Version.AtLeast(2, 35)) res |= DbgDotNetRuntimeFeatures.NoAsyncStepObjectId; return res; } public ModuleId GetModuleId(DbgModule module) => engine.GetModuleId(module); public DbgDotNetRawModuleBytes GetRawModuleBytes(DbgModule module) { if (!module.IsDynamic) return DbgDotNetRawModuleBytes.None; if (Dispatcher.CheckAccess()) return GetRawModuleBytesCore(module); return GetRawModuleBytesCore2(module); DbgDotNetRawModuleBytes GetRawModuleBytesCore2(DbgModule module2) { if (!Dispatcher.TryInvokeRethrow(() => GetRawModuleBytesCore(module2), out var result)) result = DbgDotNetRawModuleBytes.None; return result; } } DbgDotNetRawModuleBytes GetRawModuleBytesCore(DbgModule module) { Dispatcher.VerifyAccess(); if (!module.IsDynamic) return DbgDotNetRawModuleBytes.None; return DbgDotNetRawModuleBytes.None;//TODO: } public bool TryGetMethodToken(DbgModule module, int methodToken, out int metadataMethodToken, out int metadataLocalVarSigTok) { if (!module.IsDynamic) { metadataMethodToken = 0; metadataLocalVarSigTok = 0; return false; } if (Dispatcher.CheckAccess()) return TryGetMethodTokenCore(module, methodToken, out metadataMethodToken, out metadataLocalVarSigTok); return TryGetMethodTokenCore2(module, methodToken, out metadataMethodToken, out metadataLocalVarSigTok); bool TryGetMethodTokenCore2(DbgModule module2, int methodToken2, out int metadataMethodToken2, out int metadataLocalVarSigTok2) { int tmpMetadataMethodToken = 0, tmpMetadataLocalVarSigTok = 0; if (!Dispatcher.TryInvokeRethrow(() => { var res = TryGetMethodTokenCore(module2, methodToken2, out var metadataMethodToken3, out var metadataLocalVarSigTok3); tmpMetadataMethodToken = metadataMethodToken3; tmpMetadataLocalVarSigTok = metadataLocalVarSigTok3; return res; }, out var result)) { metadataMethodToken2 = 0; metadataLocalVarSigTok2 = 0; return false; } metadataMethodToken2 = tmpMetadataMethodToken; metadataLocalVarSigTok2 = tmpMetadataLocalVarSigTok; return result; } } bool TryGetMethodTokenCore(DbgModule module, int methodToken, out int metadataMethodToken, out int metadataLocalVarSigTok) { Dispatcher.VerifyAccess(); //TODO: metadataMethodToken = 0; metadataLocalVarSigTok = 0; return false; } sealed class GetFrameMethodState { public bool Initialized; public DmdMethodBase? Method; } public DmdMethodBase? GetFrameMethod(DbgEvaluationInfo evalInfo) { if (Dispatcher.CheckAccess()) return GetFrameMethodCore(evalInfo); return GetFrameMethod2(evalInfo); DmdMethodBase? GetFrameMethod2(DbgEvaluationInfo evalInfo2) { Dispatcher.TryInvokeRethrow(() => GetFrameMethodCore(evalInfo2), out var result); return result; } } DmdMethodBase? GetFrameMethodCore(DbgEvaluationInfo evalInfo) { Dispatcher.VerifyAccess(); var state = evalInfo.Frame.GetOrCreateData(); if (!state.Initialized) { evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (ILDbgEngineStackFrame.TryGetEngineStackFrame(evalInfo.Frame, out var ilFrame)) { ilFrame.GetFrameMethodInfo(out var module, out var methodMetadataToken, out var genericTypeArguments, out var genericMethodArguments); // Don't throw if it fails to resolve. Callers must be able to handle null return values state.Method = TryGetMethod(module, methodMetadataToken, genericTypeArguments, genericMethodArguments); } state.Initialized = true; } return state.Method; } static DmdMethodBase? TryGetMethod(DmdModule module, int methodMetadataToken, IList genericTypeArguments, IList genericMethodArguments) { var method = module?.ResolveMethod(methodMetadataToken, (IList?)null, null, DmdResolveOptions.None); if (method is not null) { if (genericTypeArguments.Count != 0) { var type = method.ReflectedType!.MakeGenericType(genericTypeArguments); method = type.GetMethod(method.Module, method.MetadataToken, throwOnError: true)!; } if (genericMethodArguments.Count != 0) method = ((DmdMethodInfo)method).MakeGenericMethod(genericMethodArguments); } return method; } TypeMirror GetType(DbgEvaluationInfo evalInfo, DmdType type) => MonoDebugTypeCreator.GetType(engine, type, engine.CreateMonoTypeLoader(evalInfo)); public DbgDotNetValue? LoadFieldAddress(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field) => null; public DbgDotNetValueResult LoadField(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field) { if (Dispatcher.CheckAccess()) return LoadFieldCore(evalInfo, obj, field); return LoadField2(evalInfo, obj, field); DbgDotNetValueResult LoadField2(DbgEvaluationInfo evalInfo2, DbgDotNetValue? obj2, DmdFieldInfo field2) { if (!Dispatcher.TryInvokeRethrow(() => LoadFieldCore(evalInfo2, obj2, field2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult LoadFieldCore(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field) { Dispatcher.VerifyAccess(); try { var info = GetFieldValueLocationCore(evalInfo, obj, field); if (info.errorMessage is not null) return DbgDotNetValueResult.CreateError(info.errorMessage); return DbgDotNetValueResult.Create(engine.CreateDotNetValue_MonoDebug(info.valueLocation!)); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } (ValueLocation? valueLocation, string? errorMessage) GetFieldValueLocationCore(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field) { if (!ILDbgEngineStackFrame.TryGetEngineStackFrame(evalInfo.Frame, out var ilFrame)) return (null, PredefinedEvaluationErrorMessages.InternalDebuggerError); var fieldDeclType = field.DeclaringType!; bool canNotAccessField = !engine.MonoVirtualMachine.Version.AtLeast(2, 15) && fieldDeclType.ContainsGenericParameters; var monoFieldDeclType = GetType(evalInfo, fieldDeclType); if (obj is null) { if (!field.IsStatic) return (null, PredefinedEvaluationErrorMessages.InternalDebuggerError); if (field.IsLiteral) { var monoValue = MonoValueFactory.TryCreateSyntheticValue(engine, monoFieldDeclType.Assembly.Domain, field.FieldType, field.GetRawConstantValue()) ?? new PrimitiveValue(monoFieldDeclType.VirtualMachine, ElementType.Object, null); return (new NoValueLocation(field.FieldType, monoValue), null); } else { if (canNotAccessField) return (null, dnSpy_Debugger_DotNet_Mono_Resources.Error_CannotAccessMemberRuntimeLimitations); var monoField = MemberMirrorUtils.GetMonoField(monoFieldDeclType, field); InitializeStaticConstructor(evalInfo, ilFrame, fieldDeclType, monoFieldDeclType); return (new StaticFieldValueLocation(field.FieldType, ilFrame.MonoFrame.Thread, monoField), null); } } else { if (field.IsStatic) return (null, PredefinedEvaluationErrorMessages.InternalDebuggerError); var objImp = obj as DbgDotNetValueImpl ?? throw new InvalidOperationException(); var monoField = MemberMirrorUtils.GetMonoField(monoFieldDeclType, field); switch (objImp.Value) { case ObjectMirror om: if (canNotAccessField) return (null, dnSpy_Debugger_DotNet_Mono_Resources.Error_CannotAccessMemberRuntimeLimitations); return (new ReferenceTypeFieldValueLocation(field.FieldType, om, monoField), null); case StructMirror sm: return (new ValueTypeFieldValueLocation(field.FieldType, objImp.ValueLocation, sm, monoField), null); case PrimitiveValue pv: return (null, "NYI");//TODO: default: // Unreachable return (null, PredefinedEvaluationErrorMessages.InternalDebuggerError); } } } sealed class StaticConstructorInitializedState { public volatile int Initialized; } void InitializeStaticConstructor(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, DmdType type, TypeMirror monoType) { if (engine.CheckFuncEval(evalInfo.Context) is not null) return; var state = type.GetOrCreateData(); if (state.Initialized > 0 || Interlocked.Exchange(ref state.Initialized, 1) != 0) return; if (engine.MonoVirtualMachine.Version.AtLeast(2, 23) && monoType.IsInitialized) return; var cctor = type.TypeInitializer; if (cctor is not null) { var fields = type.DeclaredFields; for (int i = 0; i < fields.Count; i++) { var field = fields[i]; if (!field.IsStatic || field.IsLiteral) continue; var monoField = MemberMirrorUtils.GetMonoField(monoType, fields, i); Value fieldValue; try { fieldValue = monoType.GetValue(monoField, ilFrame.MonoFrame.Thread); } catch { break; } if (fieldValue is not null) { if (fieldValue is PrimitiveValue pv && pv.Value is null) continue; if (field.FieldType.IsValueType) { if (!IsZero(fieldValue, 0)) return; } else { // It's a reference type and not null, so the field has been initialized return; } } } } DbgDotNetValue? dnObjValue = null; try { var reflectionAppDomain = type.AppDomain; var monoObjValue = monoType.GetTypeObject(); var objValueType = engine.GetReflectionType(reflectionAppDomain, monoObjValue.Type, null); var objValueLocation = new NoValueLocation(objValueType, monoObjValue); dnObjValue = engine.CreateDotNetValue_MonoDebug(objValueLocation); RuntimeHelpersRunClassConstructor(evalInfo, type, dnObjValue); } finally { dnObjValue?.Dispose(); } } // Calls System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(): // RuntimeHelpers.RunClassConstructor(obj.GetType().TypeHandle); bool RuntimeHelpersRunClassConstructor(DbgEvaluationInfo evalInfo, DmdType type, DbgDotNetValue objValue) { DbgDotNetValueResult typeHandleRes = default; DbgDotNetValueResult res = default; try { var reflectionAppDomain = type.AppDomain; var runtimeTypeHandleType = reflectionAppDomain.GetWellKnownType(DmdWellKnownType.System_RuntimeTypeHandle, isOptional: true); Debug2.Assert(runtimeTypeHandleType is not null); if (runtimeTypeHandleType is null) return false; var getTypeHandleMethod = objValue.Type.GetMethod("get_" + nameof(Type.TypeHandle), DmdSignatureCallingConvention.Default | DmdSignatureCallingConvention.HasThis, 0, runtimeTypeHandleType, Array.Empty(), throwOnError: false); Debug2.Assert(getTypeHandleMethod is not null); if (getTypeHandleMethod is null) return false; typeHandleRes = engine.FuncEvalCall_MonoDebug(evalInfo, getTypeHandleMethod, objValue, Array.Empty(), DbgDotNetInvokeOptions.None, false); if (typeHandleRes.Value is null || typeHandleRes.ValueIsException) return false; var runtimeHelpersType = reflectionAppDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_CompilerServices_RuntimeHelpers, isOptional: true); var runClassConstructorMethod = runtimeHelpersType?.GetMethod(nameof(RuntimeHelpers.RunClassConstructor), DmdSignatureCallingConvention.Default, 0, reflectionAppDomain.System_Void, new[] { runtimeTypeHandleType }, throwOnError: false); Debug2.Assert(runClassConstructorMethod is not null); if (runClassConstructorMethod is null) return false; res = engine.FuncEvalCall_MonoDebug(evalInfo, runClassConstructorMethod, null, new[] { typeHandleRes.Value }, DbgDotNetInvokeOptions.None, false); return !res.HasError && !res.ValueIsException; } finally { typeHandleRes.Value?.Dispose(); res.Value?.Dispose(); } } bool IsZero(Value value, int recursionCounter) { if (recursionCounter > 100) return false; if (value is PrimitiveValue pv) { if (pv.Value is null) return true; switch (pv.Type) { case ElementType.Boolean: return !(bool)pv.Value; case ElementType.Char: return (char)pv.Value == '\0'; case ElementType.I1: return (sbyte)pv.Value == 0; case ElementType.U1: return (byte)pv.Value == 0; case ElementType.I2: return (short)pv.Value == 0; case ElementType.U2: return (ushort)pv.Value == 0; case ElementType.I4: return (int)pv.Value == 0; case ElementType.U4: return (uint)pv.Value == 0; case ElementType.I8: return (long)pv.Value == 0; case ElementType.U8: return (ulong)pv.Value == 0; case ElementType.R4: return (float)pv.Value == 0; case ElementType.R8: return (double)pv.Value == 0; case ElementType.I: case ElementType.U: case ElementType.Ptr: return (long)pv.Value == 0; case ElementType.Object: return true;// It's a null value default: throw new InvalidOperationException(); } } if (value is StructMirror sm) { foreach (var f in sm.Fields) { if (!IsZero(f, recursionCounter + 1)) return false; } return true; } Debug.Assert(value is ObjectMirror); // It's a non-null object reference return false; } public string? StoreField(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field, object? value) { if (Dispatcher.CheckAccess()) return StoreFieldCore(evalInfo, obj, field, value); return StoreField2(evalInfo, obj, field, value); string? StoreField2(DbgEvaluationInfo evalInfo2, DbgDotNetValue? obj2, DmdFieldInfo field2, object? value2) { if (!Dispatcher.TryInvokeRethrow(() => StoreFieldCore(evalInfo2, obj2, field2, value2), out var result)) result = DispatcherConstants.ProcessExitedError; return result; } } string? StoreFieldCore(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdFieldInfo field, object? value) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); try { var info = GetFieldValueLocationCore(evalInfo, obj, field); if (info.errorMessage is not null) return info.errorMessage; var res = engine.CreateMonoValue_MonoDebug(evalInfo, value, field.FieldType); if (res.ErrorMessage is not null) return res.ErrorMessage; return info.valueLocation!.Store(res.Value!); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return PredefinedEvaluationErrorMessages.InternalDebuggerError; } } public DbgDotNetValueResult Call(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdMethodBase method, object?[] arguments, DbgDotNetInvokeOptions invokeOptions) { if (Dispatcher.CheckAccess()) return CallCore(evalInfo, obj, method, arguments, invokeOptions); return Call2(evalInfo, obj, method, arguments, invokeOptions); DbgDotNetValueResult Call2(DbgEvaluationInfo evalInfo2, DbgDotNetValue? obj2, DmdMethodBase method2, object?[] arguments2, DbgDotNetInvokeOptions invokeOptions2) { if (!Dispatcher.TryInvokeRethrow(() => CallCore(evalInfo2, obj2, method2, arguments2, invokeOptions2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CallCore(DbgEvaluationInfo evalInfo, DbgDotNetValue? obj, DmdMethodBase method, object?[] arguments, DbgDotNetInvokeOptions invokeOptions) { Dispatcher.VerifyAccess(); try { var type = method.DeclaringType!; if (type.IsConstructedGenericType) type = type.GetGenericTypeDefinition(); var typeName = DmdTypeName.Create(type); if (DmdWellKnownTypeUtils.TryGetWellKnownType(typeName, out var wellKnownType)) { if (classHooks.TryGetValue(wellKnownType, out var hook) && type == type.AppDomain.GetWellKnownType(wellKnownType, isOptional: true)) { var res = hook.Call(obj, method, arguments); if (res is not null) return DbgDotNetValueResult.Create(res); } } return engine.FuncEvalCall_MonoDebug(evalInfo, method, obj, arguments, invokeOptions, newObj: false); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } public DbgDotNetValueResult CreateInstance(DbgEvaluationInfo evalInfo, DmdConstructorInfo ctor, object?[] arguments, DbgDotNetInvokeOptions invokeOptions) { if (Dispatcher.CheckAccess()) return CreateInstanceCore(evalInfo, ctor, arguments, invokeOptions); return CreateInstance2(evalInfo, ctor, arguments, invokeOptions); DbgDotNetValueResult CreateInstance2(DbgEvaluationInfo evalInfo2, DmdConstructorInfo ctor2, object?[] arguments2, DbgDotNetInvokeOptions invokeOptions2) { if (!Dispatcher.TryInvokeRethrow(() => CreateInstanceCore(evalInfo2, ctor2, arguments2, invokeOptions2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CreateInstanceCore(DbgEvaluationInfo evalInfo, DmdConstructorInfo ctor, object?[] arguments, DbgDotNetInvokeOptions invokeOptions) { Dispatcher.VerifyAccess(); try { var type = ctor.DeclaringType!; if (type.IsConstructedGenericType) type = type.GetGenericTypeDefinition(); var typeName = DmdTypeName.Create(type); if (DmdWellKnownTypeUtils.TryGetWellKnownType(typeName, out var wellKnownType)) { if (classHooks.TryGetValue(wellKnownType, out var hook) && type == type.AppDomain.GetWellKnownType(wellKnownType, isOptional: true)) { var res = hook.CreateInstance(ctor, arguments); if (res is not null) return DbgDotNetValueResult.Create(res); } } return engine.FuncEvalCall_MonoDebug(evalInfo, ctor, null, arguments, invokeOptions, newObj: true); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } public DbgDotNetValueResult CreateInstanceNoConstructor(DbgEvaluationInfo evalInfo, DmdType type) { if (Dispatcher.CheckAccess()) return CreateInstanceNoConstructorCore(evalInfo, type); return CreateInstanceNoConstructor2(evalInfo, type); DbgDotNetValueResult CreateInstanceNoConstructor2(DbgEvaluationInfo evalInfo2, DmdType type2) { if (!Dispatcher.TryInvokeRethrow(() => CreateInstanceNoConstructorCore(evalInfo2, type2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CreateInstanceNoConstructorCore(DbgEvaluationInfo evalInfo, DmdType type) { Dispatcher.VerifyAccess(); try { if (type.IsValueType) return CreateValueTypeInstanceNoConstructorCore(evalInfo, type); return CreateReferenceTypeInstanceNoConstructorCore(evalInfo, type); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } DbgDotNetValueResult CreateValueTypeInstanceNoConstructorCore(DbgEvaluationInfo evalInfo, DmdType type) { var structMirror = engine.CreateValueType(evalInfo, type, 0); return DbgDotNetValueResult.Create(engine.CreateDotNetValue_MonoDebug(type.AppDomain, structMirror, type)); } DbgDotNetValueResult CreateReferenceTypeInstanceNoConstructorCore(DbgEvaluationInfo evalInfo, DmdType type) { if (engine.MonoVirtualMachine.Version.AtLeast(2, 31)) { var monoType = GetType(evalInfo, type); var value = monoType.NewInstance(); return DbgDotNetValueResult.Create(engine.CreateDotNetValue_MonoDebug(type.AppDomain, value, type)); } else { var ctor = type.GetMethod(DmdConstructorInfo.ConstructorName, DmdSignatureCallingConvention.HasThis, 0, type.AppDomain.System_Void, Array.Empty(), throwOnError: false) as DmdConstructorInfo; if (ctor is null) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); return CreateInstanceCore(evalInfo, ctor, Array.Empty(), DbgDotNetInvokeOptions.None); } } public DbgDotNetValueResult CreateSZArray(DbgEvaluationInfo evalInfo, DmdType elementType, int length) { if (Dispatcher.CheckAccess()) return CreateSZArrayCore(evalInfo, elementType, length); return CreateSZArray2(evalInfo, elementType, length); DbgDotNetValueResult CreateSZArray2(DbgEvaluationInfo evalInfo2, DmdType elementType2, int length2) { if (!Dispatcher.TryInvokeRethrow(() => CreateSZArrayCore(evalInfo2, elementType2, length2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CreateSZArrayCore(DbgEvaluationInfo evalInfo, DmdType elementType, int length) { Dispatcher.VerifyAccess(); var appDomain = elementType.AppDomain; DbgDotNetValue? typeElementType = null; try { typeElementType = engine.CreateDotNetValue_MonoDebug(appDomain, GetType(evalInfo, elementType).GetTypeObject(), null); var methodCreateInstance = appDomain.System_Array.GetMethod(nameof(Array.CreateInstance), DmdSignatureCallingConvention.Default, 0, appDomain.System_Array, new[] { appDomain.System_Type, appDomain.System_Int32 }, throwOnError: true)!; return engine.FuncEvalCall_MonoDebug(evalInfo, methodCreateInstance, null, new object[] { typeElementType, length }, DbgDotNetInvokeOptions.None, false); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } finally { typeElementType?.Dispose(); } } public DbgDotNetValueResult CreateArray(DbgEvaluationInfo evalInfo, DmdType elementType, DbgDotNetArrayDimensionInfo[] dimensionInfos) { if (Dispatcher.CheckAccess()) return CreateArrayCore(evalInfo, elementType, dimensionInfos); return CreateArray2(evalInfo, elementType, dimensionInfos); DbgDotNetValueResult CreateArray2(DbgEvaluationInfo evalInfo2, DmdType elementType2, DbgDotNetArrayDimensionInfo[] dimensionInfos2) { if (!Dispatcher.TryInvokeRethrow(() => CreateArrayCore(evalInfo2, elementType2, dimensionInfos2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CreateArrayCore(DbgEvaluationInfo evalInfo, DmdType elementType, DbgDotNetArrayDimensionInfo[] dimensionInfos) { Dispatcher.VerifyAccess(); var appDomain = elementType.AppDomain; DbgDotNetValue? typeElementType = null; try { var lengths = new int[dimensionInfos.Length]; var lowerBounds = new int[dimensionInfos.Length]; for (int i = 0; i < dimensionInfos.Length; i++) { lengths[i] = (int)dimensionInfos[i].Length; lowerBounds[i] = dimensionInfos[i].BaseIndex; } typeElementType = engine.CreateDotNetValue_MonoDebug(appDomain, GetType(evalInfo, elementType).GetTypeObject(), null); var methodCreateInstance = appDomain.System_Array.GetMethod(nameof(Array.CreateInstance), DmdSignatureCallingConvention.Default, 0, appDomain.System_Array, new[] { appDomain.System_Type, appDomain.System_Int32.MakeArrayType(), appDomain.System_Int32.MakeArrayType() }, throwOnError: true)!; return engine.FuncEvalCall_MonoDebug(evalInfo, methodCreateInstance, null, new object[] { typeElementType, lengths, lowerBounds }, DbgDotNetInvokeOptions.None, false); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } finally { typeElementType?.Dispose(); } } public DbgDotNetAliasInfo[] GetAliases(DbgEvaluationInfo evalInfo) { if (Dispatcher.CheckAccess()) return GetAliasesCore(evalInfo); return GetAliases2(evalInfo); DbgDotNetAliasInfo[] GetAliases2(DbgEvaluationInfo evalInfo2) { if (!Dispatcher.TryInvokeRethrow(() => GetAliasesCore(evalInfo2), out var result)) result = Array.Empty(); return result; } } DbgDotNetAliasInfo[] GetAliasesCore(DbgEvaluationInfo evalInfo) { Dispatcher.VerifyAccess(); DbgDotNetValue? exception = null; DbgDotNetValue? stowedException = null; var returnValues = Array.Empty(); try { exception = GetExceptionCore(evalInfo, DbgDotNetRuntimeConstants.ExceptionId); stowedException = GetStowedExceptionCore(evalInfo, DbgDotNetRuntimeConstants.StowedExceptionId); returnValues = GetReturnValuesCore(evalInfo); int count = (exception is not null ? 1 : 0) + (stowedException is not null ? 1 : 0) + returnValues.Length + (returnValues.Length != 0 ? 1 : 0); if (count == 0) return Array.Empty(); var res = new DbgDotNetAliasInfo[count]; int w = 0; if (exception is not null) res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.Exception, exception.Type, DbgDotNetRuntimeConstants.ExceptionId, Guid.Empty, null); if (stowedException is not null) res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.StowedException, stowedException.Type, DbgDotNetRuntimeConstants.StowedExceptionId, Guid.Empty, null); if (returnValues.Length != 0) { res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, returnValues[returnValues.Length - 1].Value.Type, DbgDotNetRuntimeConstants.LastReturnValueId, Guid.Empty, null); foreach (var returnValue in returnValues) { Debug.Assert(returnValue.Id != DbgDotNetRuntimeConstants.LastReturnValueId); res[w++] = new DbgDotNetAliasInfo(DbgDotNetAliasInfoKind.ReturnValue, returnValue.Value.Type, returnValue.Id, Guid.Empty, null); } } if (w != res.Length) throw new InvalidOperationException(); return res; } finally { exception?.Dispose(); stowedException?.Dispose(); foreach (var rv in returnValues) rv.Value?.Dispose(); } } public DbgDotNetExceptionInfo[] GetExceptions(DbgEvaluationInfo evalInfo) { if (Dispatcher.CheckAccess()) return GetExceptionsCore(evalInfo); return GetExceptions2(evalInfo); DbgDotNetExceptionInfo[] GetExceptions2(DbgEvaluationInfo evalInfo2) { if (!Dispatcher.TryInvokeRethrow(() => GetExceptionsCore(evalInfo2), out var result)) result = Array.Empty(); return result; } } DbgDotNetExceptionInfo[] GetExceptionsCore(DbgEvaluationInfo evalInfo) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); DbgDotNetValue? exception = null; DbgDotNetValue? stowedException = null; try { exception = GetExceptionCore(evalInfo, DbgDotNetRuntimeConstants.ExceptionId); stowedException = GetStowedExceptionCore(evalInfo, DbgDotNetRuntimeConstants.StowedExceptionId); int count = (exception is not null ? 1 : 0) + (stowedException is not null ? 1 : 0); if (count == 0) return Array.Empty(); var res = new DbgDotNetExceptionInfo[count]; int w = 0; if (exception is not null) res[w++] = new DbgDotNetExceptionInfo(exception, DbgDotNetRuntimeConstants.ExceptionId, DbgDotNetExceptionInfoFlags.None); if (stowedException is not null) res[w++] = new DbgDotNetExceptionInfo(stowedException, DbgDotNetRuntimeConstants.StowedExceptionId, DbgDotNetExceptionInfoFlags.StowedException); if (w != res.Length) throw new InvalidOperationException(); return res; } catch { exception?.Dispose(); stowedException?.Dispose(); throw; } } public DbgDotNetReturnValueInfo[] GetReturnValues(DbgEvaluationInfo evalInfo) { if (Dispatcher.CheckAccess()) return GetReturnValuesCore(evalInfo); return GetReturnValues2(evalInfo); DbgDotNetReturnValueInfo[] GetReturnValues2(DbgEvaluationInfo evalInfo2) { if (!Dispatcher.TryInvokeRethrow(() => GetReturnValuesCore(evalInfo2), out var result)) result = Array.Empty(); return result; } } DbgDotNetReturnValueInfo[] GetReturnValuesCore(DbgEvaluationInfo evalInfo) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); // Not supported by mono return Array.Empty(); } public DbgDotNetValue? GetException(DbgEvaluationInfo evalInfo, uint id) { if (Dispatcher.CheckAccess()) return GetExceptionCore(evalInfo, id); return GetException2(evalInfo, id); DbgDotNetValue? GetException2(DbgEvaluationInfo evalInfo2, uint id2) { Dispatcher.TryInvokeRethrow(() => GetExceptionCore(evalInfo2, id2), out var result); return result; } } DbgDotNetValue? GetExceptionCore(DbgEvaluationInfo evalInfo, uint id) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (id != DbgDotNetRuntimeConstants.ExceptionId) return null; return engine.TryGetExceptionValue(); } public DbgDotNetValue? GetStowedException(DbgEvaluationInfo evalInfo, uint id) { if (Dispatcher.CheckAccess()) return GetStowedExceptionCore(evalInfo, id); return GetStowedException2(evalInfo, id); DbgDotNetValue? GetStowedException2(DbgEvaluationInfo evalInfo2, uint id2) { Dispatcher.TryInvokeRethrow(() => GetStowedExceptionCore(evalInfo2, id2), out var result); return result; } } DbgDotNetValue? GetStowedExceptionCore(DbgEvaluationInfo evalInfo, uint id) { Dispatcher.VerifyAccess(); return null; } public DbgDotNetValue? GetReturnValue(DbgEvaluationInfo evalInfo, uint id) { if (Dispatcher.CheckAccess()) return GetReturnValueCore(evalInfo, id); return GetReturnValue2(evalInfo, id); DbgDotNetValue? GetReturnValue2(DbgEvaluationInfo evalInfo2, uint id2) { Dispatcher.TryInvokeRethrow(() => GetReturnValueCore(evalInfo2, id2), out var result); return result; } } DbgDotNetValue? GetReturnValueCore(DbgEvaluationInfo evalInfo, uint id) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); // Not supported by mono return null; } public DbgDotNetValueResult GetLocalValue(DbgEvaluationInfo evalInfo, uint index) { if (Dispatcher.CheckAccess()) return GetLocalValueCore(evalInfo, index); return GetLocalValue2(evalInfo, index); DbgDotNetValueResult GetLocalValue2(DbgEvaluationInfo evalInfo2, uint index2) { if (!Dispatcher.TryInvokeRethrow(() => GetLocalValueCore(evalInfo2, index2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult GetLocalValueCore(DbgEvaluationInfo evalInfo, uint index) { Dispatcher.VerifyAccess(); try { var info = GetLocalValueLocationCore(evalInfo, index); if (info.errorMessage is not null) return DbgDotNetValueResult.CreateError(info.errorMessage); return DbgDotNetValueResult.Create(engine.CreateDotNetValue_MonoDebug(info.valueLocation!)); } catch (InvalidStackFrameException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); } catch (AbsentInformationException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } (ValueLocation? valueLocation, string? errorMessage) GetLocalValueLocationCore(DbgEvaluationInfo evalInfo, uint index) { if (!ILDbgEngineStackFrame.TryGetEngineStackFrame(evalInfo.Frame, out var ilFrame)) throw new InvalidOperationException(); var monoFrame = ilFrame.MonoFrame; var locals = monoFrame.Method.GetLocals(); if ((uint)index >= (uint)locals.Length) return (null, PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); var local = locals[(int)index]; var reflectionAppDomain = ilFrame.GetReflectionModule().AppDomain; var method = GetFrameMethodCore(evalInfo); var localVars = GetCachedMethodBody(method)?.LocalVariables; var localType = localVars is not null && (uint)index < (uint)localVars.Count ? localVars[(int)index].LocalType : null; if (localType is not null && localType.IsByRef) localType = localType.GetElementType(); var type = engine.GetReflectionType(reflectionAppDomain, local.Type, localType); type = AddByRefIfNeeded(type, localVars, index); return (new LocalValueLocation(type, ilFrame, (int)index), null); } sealed class CachedMethodBodyState { public readonly DmdMethodBody? Body; public CachedMethodBodyState(DmdMethodBody? body) => Body = body; } static DmdMethodBody? GetCachedMethodBody(DmdMethodBase? method) { if (method is null) return null; if (method.TryGetData(out CachedMethodBodyState? state)) return state.Body; return CreateCachedMethodBody(method); DmdMethodBody? CreateCachedMethodBody(DmdMethodBase method2) => method2.GetOrCreateData(() => new CachedMethodBodyState(method2.GetMethodBody())).Body; } static DmdType AddByRefIfNeeded(DmdType type, ReadOnlyCollection? locals, uint index) { if (type.IsByRef) return type; if (locals is null || index >= (uint)locals.Count) return type; if (locals[(int)index].LocalType.IsByRef) return type.MakeByRefType(); return type; } static DmdType AddByRefIfNeeded(DmdType type, ReadOnlyCollection? types, uint index) { if (type.IsByRef) return type; if (types is null || index >= (uint)types.Count) return type; if (types[(int)index].IsByRef) return type.MakeByRefType(); return type; } public DbgDotNetValueResult GetParameterValue(DbgEvaluationInfo evalInfo, uint index) { if (Dispatcher.CheckAccess()) return GetParameterValueCore(evalInfo, index); return GetParameterValue2(evalInfo, index); DbgDotNetValueResult GetParameterValue2(DbgEvaluationInfo evalInfo2, uint index2) { if (!Dispatcher.TryInvokeRethrow(() => GetParameterValueCore(evalInfo2, index2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult GetParameterValueCore(DbgEvaluationInfo evalInfo, uint index) { Dispatcher.VerifyAccess(); try { var info = GetParameterValueLocationCore(evalInfo, index); if (info.errorMessage is not null) return DbgDotNetValueResult.CreateError(info.errorMessage); return DbgDotNetValueResult.Create(engine.CreateDotNetValue_MonoDebug(info.valueLocation!)); } catch (InvalidStackFrameException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); } catch (AbsentInformationException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } (ValueLocation? valueLocation, string? errorMessage) GetParameterValueLocationCore(DbgEvaluationInfo evalInfo, uint index) { Dispatcher.VerifyAccess(); if (!ILDbgEngineStackFrame.TryGetEngineStackFrame(evalInfo.Frame, out var ilFrame)) throw new InvalidOperationException(); DmdType type; var monoFrame = ilFrame.MonoFrame; var reflectionAppDomain = ilFrame.GetReflectionModule().AppDomain; if (!monoFrame.Method.IsStatic) { if (index == 0) { type = engine.GetReflectionType(reflectionAppDomain, monoFrame.Method.DeclaringType, null); if (type.IsValueType) type = type.MakeByRefType(); return (new ThisValueLocation(type, ilFrame), null); } index--; } var parameters = monoFrame.Method.GetParameters(); if ((uint)index >= (uint)parameters.Length) return (null, PredefinedEvaluationErrorMessages.CannotReadLocalOrArgumentMaybeOptimizedAway); var parameter = parameters[(int)index]; var method = GetFrameMethodCore(evalInfo); var paramTypes = method?.GetMethodSignature().GetParameterTypes(); var paramType = paramTypes is not null && (uint)index < (uint)paramTypes.Count ? paramTypes[(int)index] : null; if (paramType is not null && paramType.IsByRef) paramType = paramType.GetElementType(); type = engine.GetReflectionType(reflectionAppDomain, parameter.ParameterType, paramType); type = AddByRefIfNeeded(type, paramTypes, index); return (new ArgumentValueLocation(type, ilFrame, (int)index), null); } public string? SetLocalValue(DbgEvaluationInfo evalInfo, uint index, DmdType targetType, object? value) { if (Dispatcher.CheckAccess()) return SetLocalValueCore(evalInfo, index, targetType, value); return SetLocalValue2(evalInfo, index, targetType, value); string? SetLocalValue2(DbgEvaluationInfo evalInfo2, uint index2, DmdType targetType2, object? value2) { if (!Dispatcher.TryInvokeRethrow(() => SetLocalValueCore(evalInfo2, index2, targetType2, value2), out var result)) result = DispatcherConstants.ProcessExitedError; return result; } } string? SetLocalValueCore(DbgEvaluationInfo evalInfo, uint index, DmdType targetType, object? value) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); try { var info = GetLocalValueLocationCore(evalInfo, index); if (info.errorMessage is not null) return info.errorMessage; var res = engine.CreateMonoValue_MonoDebug(evalInfo, value, targetType); if (res.ErrorMessage is not null) return res.ErrorMessage; return info.valueLocation!.Store(res.Value!); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return PredefinedEvaluationErrorMessages.InternalDebuggerError; } } public string? SetParameterValue(DbgEvaluationInfo evalInfo, uint index, DmdType targetType, object? value) { if (Dispatcher.CheckAccess()) return SetParameterValueCore(evalInfo, index, targetType, value); return SetParameterValue2(evalInfo, index, targetType, value); string? SetParameterValue2(DbgEvaluationInfo evalInfo2, uint index2, DmdType targetType2, object? value2) { if (!Dispatcher.TryInvokeRethrow(() => SetParameterValueCore(evalInfo2, index2, targetType2, value2), out var result)) result = DispatcherConstants.ProcessExitedError; return result; } } string? SetParameterValueCore(DbgEvaluationInfo evalInfo, uint index, DmdType targetType, object? value) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); try { var info = GetParameterValueLocationCore(evalInfo, index); if (info.errorMessage is not null) return info.errorMessage; var res = engine.CreateMonoValue_MonoDebug(evalInfo, value, targetType); if (res.ErrorMessage is not null) return res.ErrorMessage; return info.valueLocation!.Store(res.Value!); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return PredefinedEvaluationErrorMessages.InternalDebuggerError; } } public DbgDotNetValue? GetLocalValueAddress(DbgEvaluationInfo evalInfo, uint index, DmdType targetType) => null; public DbgDotNetValue? GetParameterValueAddress(DbgEvaluationInfo evalInfo, uint index, DmdType targetType) => null; public DbgDotNetValueResult CreateValue(DbgEvaluationInfo evalInfo, object? value) { if (Dispatcher.CheckAccess()) return CreateValueCore(evalInfo, value); return CreateValue2(evalInfo, value); DbgDotNetValueResult CreateValue2(DbgEvaluationInfo evalInfo2, object? value2) { if (!Dispatcher.TryInvokeRethrow(() => CreateValueCore(evalInfo2, value2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult CreateValueCore(DbgEvaluationInfo evalInfo, object? value) { Dispatcher.VerifyAccess(); try { return engine.CreateValue_MonoDebug(evalInfo, value); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } public DbgDotNetValueResult Box(DbgEvaluationInfo evalInfo, object? value) { if (Dispatcher.CheckAccess()) return BoxCore(evalInfo, value); return Box2(evalInfo, value); DbgDotNetValueResult Box2(DbgEvaluationInfo evalInfo2, object? value2) { if (!Dispatcher.TryInvokeRethrow(() => BoxCore(evalInfo2, value2), out var result)) result = DbgDotNetValueResult.CreateError(DispatcherConstants.ProcessExitedError); return result; } } DbgDotNetValueResult BoxCore(DbgEvaluationInfo evalInfo, object? value) { Dispatcher.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); DbgDotNetValueResult res = default; try { res = CreateValueCore(evalInfo, value); if (res.ErrorMessage is not null) return res; var boxedValue = res.Value!.Box(evalInfo); if (boxedValue is not null) return boxedValue.Value; return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } finally { res.Value?.Dispose(); } } public bool CanCreateObjectId(DbgDotNetValue value) => (value as DbgDotNetValueImpl)?.Value is ObjectMirror; public DbgDotNetObjectId? CreateObjectId(DbgDotNetValue value, uint id) { var valueImpl = value as DbgDotNetValueImpl; if (valueImpl is null) return null; if (Dispatcher.CheckAccess()) return CreateObjectIdCore(valueImpl, id); return CreateObjectId2(valueImpl, id); DbgDotNetObjectId? CreateObjectId2(DbgDotNetValueImpl value2, uint id2) { Dispatcher.TryInvokeRethrow(() => CreateObjectIdCore(value2, id2), out var result); return result; } } DbgDotNetObjectId? CreateObjectIdCore(DbgDotNetValueImpl value, uint id) { Dispatcher.VerifyAccess(); try { if (!engine.IsPaused) return null; var valueObjectMirror = value.Value as ObjectMirror; if (valueObjectMirror is null) return null; var appDomain = value.Type.AppDomain; var gcHandleType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_GCHandle, isOptional: true); Debug2.Assert(gcHandleType is not null); if (gcHandleType is null) return null; var allocMethod = gcHandleType.GetMethod(nameof(System.Runtime.InteropServices.GCHandle.Alloc), DmdSignatureCallingConvention.Default, 0, gcHandleType, new[] { appDomain.System_Object }, throwOnError: false); Debug2.Assert(allocMethod is not null); if (allocMethod is null) return null; var res = engine.FuncEvalCall_AnyThread_MonoDebug(allocMethod, null, new[] { value }, DbgDotNetInvokeOptions.None, newObj: false); Debug.Assert(res.IsNormalResult); if (!res.IsNormalResult) { res.Value?.Dispose(); return null; } return new DbgDotNetObjectIdImpl(this, id, res.Value!, valueObjectMirror, appDomain); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return null; } } internal void FreeObjectId(DbgDotNetObjectIdImpl objectId) { if (Dispatcher.CheckAccess()) FreeObjectIdCore(objectId); else FreeObjectId2(objectId); void FreeObjectId2(DbgDotNetObjectIdImpl objectId2) { if (!Dispatcher.TryBeginInvoke(() => FreeObjectIdCore(objectId2))) { // process has exited } } } void FreeObjectIdCore(DbgDotNetObjectIdImpl objectId) { Dispatcher.VerifyAccess(); engine.AddExecOnPause(() => FreeObjectId_Paused(objectId)); } void FreeObjectId_Paused(DbgDotNetObjectIdImpl objectId) { Dispatcher.VerifyAccess(); if (engine.DbgRuntime.IsClosed) return; Debug.Assert(engine.IsPaused); var appDomain = objectId.ReflectionAppDomain; var gcHandleType = appDomain.GetWellKnownType(DmdWellKnownType.System_Runtime_InteropServices_GCHandle, isOptional: true); Debug2.Assert(gcHandleType is not null); if (gcHandleType is null) return; var freeMethod = gcHandleType.GetMethod(nameof(System.Runtime.InteropServices.GCHandle.Free), DmdSignatureCallingConvention.HasThis, 0, appDomain.System_Void, Array.Empty(), throwOnError: false); Debug2.Assert(freeMethod is not null); if (freeMethod is null) return; var res = engine.FuncEvalCall_AnyThread_MonoDebug(freeMethod, objectId.GCHandleValue, Array.Empty(), DbgDotNetInvokeOptions.None, newObj: false); Debug.Assert(res.IsNormalResult); res.Value?.Dispose(); } public bool Equals(DbgDotNetObjectId objectId, DbgDotNetValue value) { var objectIdImpl = objectId as DbgDotNetObjectIdImpl; var valueImpl = value as DbgDotNetValueImpl; if (objectIdImpl is null || valueImpl is null) return false; if (Dispatcher.CheckAccess()) return EqualsCore(objectIdImpl, valueImpl); return Equals2(objectIdImpl, valueImpl); bool Equals2(DbgDotNetObjectIdImpl objectId2, DbgDotNetValueImpl value2) { Dispatcher.TryInvokeRethrow(() => EqualsCore(objectId2, value2), out var result); return result; } } readonly struct EquatableValue { public readonly ulong Address; readonly ObjectMirror? value; public EquatableValue(ObjectMirror? value) { if (value is null) Address = 0; else { try { Address = (ulong)value.Address; } catch (ObjectCollectedException) { Address = 0; } } this.value = value; } public bool Equals2(in EquatableValue other) => Address != 0 && Address == other.Address; public bool? Equals3(in EquatableValue other) => Address == 0 && other.Address == 0 ? (bool?)null : Address == other.Address; // Value must be stable, so we can't use Address (obj could get moved by the GC). It's used by dictionaries. public new int GetHashCode() => Address == 0 ? 0 : value?.Type.GetHashCode() ?? 0; } bool EqualsCore(DbgDotNetObjectIdImpl objectId, DbgDotNetValueImpl value) { Dispatcher.VerifyAccess(); var objectIdValue = objectId.Value; var valueObject = value.Value as ObjectMirror; if (valueObject is null) return false; if (objectIdValue == valueObject) return true; var v1 = GetEquatableValue(objectIdValue); var v2 = GetEquatableValue(valueObject); return v1.Equals2(v2); } static EquatableValue GetEquatableValue(ObjectMirror? value) => new EquatableValue(value); public int GetHashCode(DbgDotNetObjectId objectId) { var objectIdImpl = objectId as DbgDotNetObjectIdImpl; if (objectIdImpl is null) return 0; if (Dispatcher.CheckAccess()) return GetHashCodeCore(objectIdImpl); return GetHashCode2(objectIdImpl); int GetHashCode2(DbgDotNetObjectIdImpl objectId2) { Dispatcher.TryInvokeRethrow(() => GetHashCodeCore(objectId2), out var result); return result; } } int GetHashCodeCore(DbgDotNetObjectIdImpl objectId) { Dispatcher.VerifyAccess(); return GetEquatableValue(objectId.Value).GetHashCode(); } public int GetHashCode(DbgDotNetValue value) { var valueImpl = value as DbgDotNetValueImpl; if (valueImpl is null) return 0; if (Dispatcher.CheckAccess()) return GetHashCodeCore(valueImpl); return GetHashCode2(valueImpl); int GetHashCode2(DbgDotNetValueImpl value2) { Dispatcher.TryInvokeRethrow(() => GetHashCodeCore(value2), out var result); return result; } } int GetHashCodeCore(DbgDotNetValueImpl value) { Dispatcher.VerifyAccess(); return GetEquatableValue(value.Value as ObjectMirror).GetHashCode(); } public DbgDotNetValue? GetValue(DbgEvaluationInfo evalInfo, DbgDotNetObjectId objectId) { var objectIdImpl = objectId as DbgDotNetObjectIdImpl; if (objectIdImpl is null) throw new ArgumentException(); if (Dispatcher.CheckAccess()) return GetValueCore(evalInfo, objectIdImpl); return GetValue2(evalInfo, objectIdImpl); DbgDotNetValue GetValue2(DbgEvaluationInfo evalInfo2, DbgDotNetObjectIdImpl objectId2) { Dispatcher.TryInvokeRethrow(() => GetValueCore(evalInfo2, objectId2), out var result); return result; } } DbgDotNetValue GetValueCore(DbgEvaluationInfo evalInfo, DbgDotNetObjectIdImpl objectId) { Dispatcher.VerifyAccess(); return engine.CreateDotNetValue_MonoDebug(objectId.ReflectionAppDomain, objectId.Value, null); } public bool? Equals(DbgDotNetValue a, DbgDotNetValue b) { if (a == b) return true; if (a.Type != b.Type) return false; var ai = a as DbgDotNetValueImpl; var bi = b as DbgDotNetValueImpl; if (ai is null || bi is null) { // If they're equal, they're both null return ai == bi ? (bool?)null : false; } if (Dispatcher.CheckAccess()) return EqualsCore(ai, bi); return Equals2(ai, bi); bool? Equals2(DbgDotNetValueImpl a2, DbgDotNetValueImpl b2) { if (!Dispatcher.TryInvokeRethrow(() => EqualsCore(a2, b2), out var result)) result = false; return result; } } bool? EqualsCore(DbgDotNetValueImpl a, DbgDotNetValueImpl b) { Dispatcher.VerifyAccess(); return GetEquatableValue(a.Value as ObjectMirror).Equals3(GetEquatableValue(b.Value as ObjectMirror)); } public bool TryGetNativeCode(DbgStackFrame frame, out DbgDotNetNativeCode nativeCode) { nativeCode = default; return false; } public bool TryGetNativeCode(DmdMethodBase method, out DbgDotNetNativeCode nativeCode) { nativeCode = default; return false; } public bool TryGetSymbol(ulong address, out SymbolResolverResult result) { result = default; return false; } protected override void CloseCore(DbgDispatcher dispatcher) { } } }