2021-09-20 18:20:01 +02:00

1283 lines
53 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
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<DmdWellKnownType, ClassHook> 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<DmdWellKnownType, ClassHook>();
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<GetFrameMethodState>();
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<DmdType> genericTypeArguments, IList<DmdType> genericMethodArguments) {
var method = module?.ResolveMethod(methodMetadataToken, (IList<DmdType>?)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<StaticConstructorInitializedState>();
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<DmdType>(), throwOnError: false);
Debug2.Assert(getTypeHandleMethod is not null);
if (getTypeHandleMethod is null)
return false;
typeHandleRes = engine.FuncEvalCall_MonoDebug(evalInfo, getTypeHandleMethod, objValue, Array.Empty<object>(), 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<DmdType>(), throwOnError: false) as DmdConstructorInfo;
if (ctor is null)
return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError);
return CreateInstanceCore(evalInfo, ctor, Array.Empty<object>(), 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<DbgDotNetAliasInfo>();
return result;
}
}
DbgDotNetAliasInfo[] GetAliasesCore(DbgEvaluationInfo evalInfo) {
Dispatcher.VerifyAccess();
DbgDotNetValue? exception = null;
DbgDotNetValue? stowedException = null;
var returnValues = Array.Empty<DbgDotNetReturnValueInfo>();
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<DbgDotNetAliasInfo>();
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<DbgDotNetExceptionInfo>();
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<DbgDotNetExceptionInfo>();
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<DbgDotNetReturnValueInfo>();
return result;
}
}
DbgDotNetReturnValueInfo[] GetReturnValuesCore(DbgEvaluationInfo evalInfo) {
Dispatcher.VerifyAccess();
evalInfo.CancellationToken.ThrowIfCancellationRequested();
// Not supported by mono
return Array.Empty<DbgDotNetReturnValueInfo>();
}
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<DmdLocalVariableInfo>? 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<DmdType>? 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<DmdType>(), throwOnError: false);
Debug2.Assert(freeMethod is not null);
if (freeMethod is null)
return;
var res = engine.FuncEvalCall_AnyThread_MonoDebug(freeMethod, objectId.GCHandleValue, Array.Empty<object>(), 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) { }
}
}