/* 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.Diagnostics; using System.Threading; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.CallStack; using dnSpy.Contracts.Debugger.DotNet.Evaluation; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Debugger.DotNet.Metadata; using dnSpy.Debugger.DotNet.Mono.Impl.Evaluation; using dnSpy.Debugger.DotNet.Mono.Properties; using Mono.Debugger.Soft; namespace dnSpy.Debugger.DotNet.Mono.Impl { sealed partial class DbgEngineImpl { internal int? OffsetToStringData { get { debuggerThread.VerifyAccess(); InitializeObjectConstantsCore_MonoDebug(); return objectConstants?.OffsetToStringData; } } internal int? OffsetToArrayData { get { debuggerThread.VerifyAccess(); InitializeObjectConstantsCore_MonoDebug(); return objectConstants?.OffsetToArrayData; } } ObjectConstants? objectConstants; bool canInitializeObjectConstants; void InitializeObjectConstants_MonoDebug() { // Don't do a thing, it sometimes hangs somewhere in mono. It's less likely to // hang if the process has run for a few seconds. // The properties above will call the next method. } void InitializeObjectConstantsCore_MonoDebug() { debuggerThread.VerifyAccess(); if (!canInitializeObjectConstants) return; if (objectConstants is not null) return; if (objectFactory is null) return; foreach (var thread in vm!.GetThreads()) { if (thread.Name == FinalizerName) continue; var factory = new ObjectConstantsFactory(objectFactory.Process, thread); //TODO: Fails on mono when TypeLoad events are used, apparently we can't func-eval until much later and we don't get an error so it times out if (factory.TryCreate(out objectConstants)) break; } } internal DbgDotNetValue CreateDotNetValue_MonoDebug(DmdAppDomain reflectionAppDomain, Value value, DmdType? realTypeOpt) { debuggerThread.VerifyAccess(); if (value is null) return new SyntheticNullValue(realTypeOpt ?? reflectionAppDomain.System_Object); DmdType type; if (value is PrimitiveValue pv) type = MonoValueTypeCreator.CreateType(this, value, reflectionAppDomain.System_Object); else if (value is StructMirror sm) type = GetReflectionType(reflectionAppDomain, sm.Type, realTypeOpt); else { Debug.Assert(value is ObjectMirror); type = GetReflectionType(reflectionAppDomain, ((ObjectMirror)value).Type, realTypeOpt); } var valueLocation = new NoValueLocation(type, value); return CreateDotNetValue_MonoDebug(valueLocation); } internal DbgDotNetValue CreateDotNetValue_MonoDebug(ValueLocation valueLocation) { debuggerThread.VerifyAccess(); var value = valueLocation.Load(); if (value is null) return new SyntheticNullValue(valueLocation.Type); var dnValue = new DbgDotNetValueImpl(this, valueLocation, value); lock (lockObj) dotNetValuesToCloseOnContinue.Add(dnValue); return dnValue; } void CloseDotNetValues_MonoDebug() { debuggerThread.VerifyAccess(); DbgDotNetValueImpl[] valuesToClose; lock (lockObj) { valuesToClose = dotNetValuesToCloseOnContinue.Count == 0 ? Array.Empty() : dotNetValuesToCloseOnContinue.ToArray(); dotNetValuesToCloseOnContinue.Clear(); } foreach (var value in valuesToClose) value.Dispose(); } internal void AddExecOnPause(Action action) { debuggerThread.VerifyAccess(); if (IsPaused) action(); else execOnPauseList.Add(action); } void RunExecOnPauseDelegates_MonoDebug() { debuggerThread.VerifyAccess(); if (execOnPauseList.Count == 0) return; var list = execOnPauseList.ToArray(); execOnPauseList.Clear(); foreach (var action in list) action(); } bool IsEvaluating => funcEvalFactory.IsEvaluating; internal int MethodInvokeCounter => funcEvalFactory.MethodInvokeCounter; sealed class EvalTimedOut { } internal DbgDotNetValueResult? CheckFuncEval(DbgEvaluationContext context) { debuggerThread.VerifyAccess(); if (!IsPaused) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CanFuncEvalOnlyWhenPaused); if (isUnhandledException) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEvalWhenUnhandledExceptionHasOccurred); if (context.ContinueContext.HasData()) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOutNowDisabled); if (IsEvaluating) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEval); return null; } void OnFuncEvalComplete(FuncEval funcEval, DbgEvaluationContext context) { if (funcEval.EvalTimedOut) context.ContinueContext.GetOrCreateData(); OnFuncEvalComplete(funcEval); } void OnFuncEvalComplete(FuncEval funcEval) { } FuncEval CreateFuncEval(DbgEvaluationContext context, ThreadMirror monoThread, CancellationToken cancellationToken) => funcEvalFactory.CreateFuncEval(a => OnFuncEvalComplete(a, context), monoThread, context.FuncEvalTimeout, suspendOtherThreads: (context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0, cancellationToken: cancellationToken); FuncEval CreateFuncEval2(DbgEvaluationContext? contextOpt, ThreadMirror monoThread, CancellationToken cancellationToken) { if (contextOpt is not null) return CreateFuncEval(contextOpt, monoThread, cancellationToken); return funcEvalFactory.CreateFuncEval(a => OnFuncEvalComplete(a), monoThread, DbgLanguage.DefaultFuncEvalTimeout, suspendOtherThreads: true, cancellationToken: cancellationToken); } Value? TryInvokeMethod(ThreadMirror thread, ObjectMirror obj, MethodMirror method, IList arguments, out bool timedOut) { debuggerThread.VerifyAccess(); if (IsEvaluating) { timedOut = true; return null; } var funcEvalTimeout = DbgLanguage.DefaultFuncEvalTimeout; const bool suspendOtherThreads = true; var cancellationToken = CancellationToken.None; try { timedOut = false; using (var funcEval = funcEvalFactory.CreateFuncEval(a => OnFuncEvalComplete(a), thread, funcEvalTimeout, suspendOtherThreads, cancellationToken)) return funcEval.CallMethod(method, obj, arguments, FuncEvalOptions.None).Result; } catch (TimeoutException) { timedOut = true; return null; } } List GetFuncEvalCallThreads(DmdAppDomain reflectionAppDomain) { debuggerThread.VerifyAccess(); var allThreads = DbgRuntime.Threads; var threads = new List(allThreads.Length); var appDomain = reflectionAppDomain.GetDebuggerAppDomain(); foreach (var t in allThreads) { if (t.AppDomain == appDomain) threads.Add(t); } threads.Sort((a, b) => GetThreadOrder(a).CompareTo(GetThreadOrder(b))); return threads; } int GetThreadOrder(DbgThread a) { if (a == DbgRuntime.Process.DbgManager.CurrentThread.Current) return 0; if (a == DbgRuntime.Process.DbgManager.CurrentThread.Break) return 1; if (a.IsMain) return 2; if (a.Kind != PredefinedThreadKinds.Finalizer) return 3; return int.MaxValue; } internal DbgDotNetValueResult FuncEvalCall_AnyThread_MonoDebug(DmdMethodBase method, DbgDotNetValue? obj, object?[] arguments, DbgDotNetInvokeOptions invokeOptions, bool newObj) { debuggerThread.VerifyAccess(); var cancellationToken = CancellationToken.None; foreach (var thread in GetFuncEvalCallThreads(method.AppDomain)) { var res = FuncEvalCallCore_MonoDebug(null, null, thread, method, obj, arguments, invokeOptions, newObj, cancellationToken); if (!res.HasError) return res; } return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } internal DbgDotNetValueResult FuncEvalCall_MonoDebug(DbgEvaluationInfo evalInfo, DmdMethodBase method, DbgDotNetValue? obj, object?[] arguments, DbgDotNetInvokeOptions invokeOptions, bool newObj) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo.Context); if (tmp is not null) return tmp.Value; return FuncEvalCallCore_MonoDebug(evalInfo.Context, evalInfo.Frame, evalInfo.Frame.Thread, method, obj, arguments, invokeOptions, newObj, evalInfo.CancellationToken); } internal MonoTypeLoader CreateMonoTypeLoader(DbgEvaluationInfo evalInfo) => new MonoTypeLoaderImpl(this, evalInfo); internal MonoTypeLoader? TryCreateMonoTypeLoader(DbgEvaluationContext? contextOpt, DbgStackFrame? frameOpt, CancellationToken cancellationToken) { if (contextOpt is null || frameOpt is null) return null; return CreateMonoTypeLoader(new DbgEvaluationInfo(contextOpt, frameOpt, cancellationToken)); } DbgDotNetValueResult FuncEvalCallCore_MonoDebug(DbgEvaluationContext? contextOpt, DbgStackFrame? frameOpt, DbgThread thread, DmdMethodBase method, DbgDotNetValue? obj, object?[] arguments, DbgDotNetInvokeOptions invokeOptions, bool newObj, CancellationToken cancellationToken) { // ReturnOutThis is only available since 2.35 so we'll special case the common case where a struct ctor // is called (CALL/CALLVIRT). We'll change it to a NEWOBJ and then copy the result to the input 'this' value. if (!newObj && obj is DbgDotNetValueImpl objImpl && method is DmdConstructorInfo ctor && ctor.ReflectedType!.IsValueType) { var res = FuncEvalCallCoreReal_MonoDebug(contextOpt, frameOpt, thread, method, null, arguments, invokeOptions, true, cancellationToken); if (res.IsNormalResult) { try { var error = objImpl.ValueLocation.Store(((DbgDotNetValueImpl)res.Value!).Value); if (error is not null) { res.Value?.Dispose(); return DbgDotNetValueResult.CreateError(error); } } catch { res.Value?.Dispose(); throw; } } return res; } else return FuncEvalCallCoreReal_MonoDebug(contextOpt, frameOpt, thread, method, obj, arguments, invokeOptions, newObj, cancellationToken); } DbgDotNetValueResult FuncEvalCallCoreReal_MonoDebug(DbgEvaluationContext? contextOpt, DbgStackFrame? frameOpt, DbgThread thread, DmdMethodBase method, DbgDotNetValue? obj, object?[] arguments, DbgDotNetInvokeOptions invokeOptions, bool newObj, CancellationToken cancellationToken) { debuggerThread.VerifyAccess(); Debug.Assert(!newObj || method.IsConstructor); Debug.Assert(method.SpecialMethodKind == DmdSpecialMethodKind.Metadata, "Methods not defined in metadata should be emulated by other code (i.e., the caller)"); if (method.SpecialMethodKind != DmdSpecialMethodKind.Metadata) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); if (!vm!.Version.AtLeast(2, 24) && method is DmdMethodInfo && method.IsConstructedGenericMethod) return DbgDotNetValueResult.CreateError(dnSpy_Debugger_DotNet_Mono_Resources.Error_RuntimeDoesNotSupportCallingGenericMethods); var funcEvalOptions = FuncEvalOptions.None; if ((invokeOptions & DbgDotNetInvokeOptions.NonVirtual) == 0 && !method.IsStatic && (method.IsVirtual || method.IsAbstract)) funcEvalOptions |= FuncEvalOptions.Virtual; MethodMirror func; DmdMethodBase calledMethod; if ((funcEvalOptions & FuncEvalOptions.Virtual) == 0 || vm.Version.AtLeast(2, 37)) func = MethodCache.GetMethod(calledMethod = method, TryCreateMonoTypeLoader(contextOpt, frameOpt, cancellationToken)); else { func = MethodCache.GetMethod(calledMethod = FindOverloadedMethod(obj?.Type, method), TryCreateMonoTypeLoader(contextOpt, frameOpt, cancellationToken)); funcEvalOptions &= ~FuncEvalOptions.Virtual; } if (!vm.Version.AtLeast(2, 15) && calledMethod.DeclaringType!.ContainsGenericParameters) return DbgDotNetValueResult.CreateError(dnSpy_Debugger_DotNet_Mono_Resources.Error_CannotAccessMemberRuntimeLimitations); var monoThread = GetThread(thread); try { using (var funcEval = CreateFuncEval2(contextOpt, monoThread, cancellationToken)) { var converter = new EvalArgumentConverter(this, funcEval, monoThread.Domain, method.AppDomain); var paramTypes = GetAllMethodParameterTypes(method.GetMethodSignature()); if (paramTypes.Count != arguments.Length) throw new InvalidOperationException(); int argsCount = arguments.Length; var args = argsCount == 0 ? Array.Empty() : new Value[argsCount]; DmdType origType; Value? hiddenThisValue; Value? createdResultValue = null; if (!method.IsStatic && !newObj) { var declType = method.DeclaringType!; if (method is DmdMethodInfo m) declType = m.GetBaseDefinition().DeclaringType!; var val = converter.Convert(obj, declType, out origType); if (val.ErrorMessage is not null) return DbgDotNetValueResult.CreateError(val.ErrorMessage); // Don't box it if it's a value type and it implements the method, eg. 1.ToString() fails without this check if (origType.IsValueType && method.DeclaringType == origType) { if (val.Value is ObjectMirror) hiddenThisValue = ValueUtils.Unbox((ObjectMirror)val.Value, origType); else hiddenThisValue = val.Value!; } else hiddenThisValue = BoxIfNeeded(monoThread.Domain, val.Value!, declType, origType); if (val.Value == hiddenThisValue && val.Value is StructMirror && vm.Version.AtLeast(2, 35)) funcEvalOptions |= FuncEvalOptions.ReturnOutThis; } else if (newObj && method.ReflectedType!.IsValueType) { if (contextOpt is not null && frameOpt is not null) { //TODO: The Mono fork Unity uses doesn't support this, it returns nothing var evalInfo = new DbgEvaluationInfo(contextOpt, frameOpt, cancellationToken); hiddenThisValue = CreateValueType(evalInfo, method.ReflectedType, 0); createdResultValue = hiddenThisValue; newObj = false; if (hiddenThisValue is StructMirror && vm.Version.AtLeast(2, 35)) funcEvalOptions |= FuncEvalOptions.ReturnOutThis; } else hiddenThisValue = null; } else hiddenThisValue = null; for (int i = 0; i < arguments.Length; i++) { var paramType = paramTypes[i]; var val = converter.Convert(arguments[i], paramType, out origType); if (val.ErrorMessage is not null) return DbgDotNetValueResult.CreateError(val.ErrorMessage); var valType = origType ?? MonoValueTypeCreator.CreateType(this, val.Value!, paramType); args[i] = BoxIfNeeded(monoThread.Domain, val.Value!, paramType, valType); } var res = newObj ? funcEval.CreateInstance(func, args, funcEvalOptions) : funcEval.CallMethod(func, hiddenThisValue, args, funcEvalOptions); if (res is null) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); if ((funcEvalOptions & FuncEvalOptions.ReturnOutThis) != 0 && res.OutThis is StructMirror outStructMirror) { var error = (obj as DbgDotNetValueImpl)?.ValueLocation.Store(outStructMirror); if (error is not null) return DbgDotNetValueResult.CreateError(error); } var returnType = (method as DmdMethodInfo)?.ReturnType ?? method.ReflectedType!; var returnValue = res.Exception ?? res.Result ?? createdResultValue ?? new PrimitiveValue(vm, ElementType.Object, null); var valueLocation = new NoValueLocation(returnType, returnValue); if (res.Exception is not null) return DbgDotNetValueResult.CreateException(CreateDotNetValue_MonoDebug(valueLocation)); return DbgDotNetValueResult.Create(CreateDotNetValue_MonoDebug(valueLocation)); } } catch (VMNotSuspendedException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEvaluateWhenThreadIsAtUnsafePoint); } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } Value BoxIfNeeded(AppDomainMirror appDomain, Value value, DmdType targetType, DmdType valueType) { if (!targetType.IsValueType && valueType.IsValueType && (value is PrimitiveValue || value is StructMirror)) return appDomain.CreateBoxedValue(value); return value; } static IList GetAllMethodParameterTypes(DmdMethodSignature sig) { if (sig.GetVarArgsParameterTypes().Count == 0) return sig.GetParameterTypes(); var list = new List(sig.GetParameterTypes().Count + sig.GetVarArgsParameterTypes().Count); list.AddRange(sig.GetParameterTypes()); list.AddRange(sig.GetVarArgsParameterTypes()); return list; } internal DbgDotNetValueResult Box_MonoDebug(DbgEvaluationInfo evalInfo, Value value, DmdType type) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo.Context); if (tmp is not null) return tmp.Value; var monoThread = GetThread(evalInfo.Frame.Thread); try { using (var funcEval = CreateFuncEval(evalInfo.Context, monoThread, evalInfo.CancellationToken)) { value = ValueUtils.MakePrimitiveValueIfPossible(value, type); var boxedValue = BoxIfNeeded(monoThread.Domain, value, type.AppDomain.System_Object, type); if (boxedValue is null) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); return DbgDotNetValueResult.Create(CreateDotNetValue_MonoDebug(type.AppDomain, boxedValue, type)); } } catch (VMNotSuspendedException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEvaluateWhenThreadIsAtUnsafePoint); } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } internal DbgDotNetValueResult CreateValue_MonoDebug(DbgEvaluationInfo evalInfo, object? value) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (value is DbgDotNetValueImpl) return DbgDotNetValueResult.Create((DbgDotNetValueImpl)value); var tmp = CheckFuncEval(evalInfo.Context); if (tmp is not null) return tmp.Value; var monoThread = GetThread(evalInfo.Frame.Thread); try { var reflectionAppDomain = evalInfo.Frame.AppDomain?.GetReflectionAppDomain() ?? throw new InvalidOperationException(); using (var funcEval = CreateFuncEval(evalInfo.Context, monoThread, evalInfo.CancellationToken)) { var converter = new EvalArgumentConverter(this, funcEval, monoThread.Domain, reflectionAppDomain); var evalRes = converter.Convert(value, reflectionAppDomain.System_Object, out var newValueType); if (evalRes.ErrorMessage is not null) return DbgDotNetValueResult.CreateError(evalRes.ErrorMessage); var resultValue = CreateDotNetValue_MonoDebug(reflectionAppDomain, evalRes.Value!, newValueType); return DbgDotNetValueResult.Create(resultValue); } } catch (VMNotSuspendedException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEvaluateWhenThreadIsAtUnsafePoint); } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } internal DbgCreateMonoValueResult CreateMonoValue_MonoDebug(DbgEvaluationInfo evalInfo, object? value, DmdType targetType) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (value is DbgDotNetValueImpl) return new DbgCreateMonoValueResult(((DbgDotNetValueImpl)value).Value); var tmp = CheckFuncEval(evalInfo.Context); if (tmp is not null) return new DbgCreateMonoValueResult(tmp.Value.ErrorMessage ?? throw new InvalidOperationException()); var monoThread = GetThread(evalInfo.Frame.Thread); try { var reflectionAppDomain = evalInfo.Frame.AppDomain?.GetReflectionAppDomain() ?? throw new InvalidOperationException(); using (var funcEval = CreateFuncEval(evalInfo.Context, monoThread, evalInfo.CancellationToken)) { var converter = new EvalArgumentConverter(this, funcEval, monoThread.Domain, reflectionAppDomain); var evalRes = converter.Convert(value, targetType, out var newValueType); if (evalRes.ErrorMessage is not null) return new DbgCreateMonoValueResult(evalRes.ErrorMessage); var newValue = evalRes.Value!; if (targetType.IsEnum && !(newValue is EnumMirror)) newValue = MonoVirtualMachine.CreateEnumMirror(MonoDebugTypeCreator.GetType(this, targetType, CreateMonoTypeLoader(evalInfo)), (PrimitiveValue)newValue); newValue = BoxIfNeeded(monoThread.Domain, newValue, targetType, newValueType); return new DbgCreateMonoValueResult(newValue); } } catch (VMNotSuspendedException) { return new DbgCreateMonoValueResult(PredefinedEvaluationErrorMessages.CantFuncEvaluateWhenThreadIsAtUnsafePoint); } catch (TimeoutException) { return new DbgCreateMonoValueResult(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return new DbgCreateMonoValueResult(PredefinedEvaluationErrorMessages.InternalDebuggerError); } } static DmdMethodBase FindOverloadedMethod(DmdType? objType, DmdMethodBase method) { if (objType is null) return method; if (!method.IsVirtual && !method.IsAbstract) return method; if (method.DeclaringType == objType) return method; var comparer = new DmdSigComparer(DmdMemberInfoEqualityComparer.DefaultMember.Options & ~DmdSigComparerOptions.CompareDeclaringType); foreach (var m in objType.Methods) { if (!m.IsVirtual) continue; if (comparer.Equals(m, method)) return m; } if (method.DeclaringType!.IsInterface) { var sig = method.GetMethodSignature(); foreach (var m in objType.Methods) { if (!m.IsVirtual) continue; //TODO: Use MethodImpl table. For now assume it's an explicitly implemented iface method if it's private and name doesn't match original name if (!m.IsPrivate || m.Name == method.Name) continue; if (comparer.Equals(m.GetMethodSignature(), sig)) return m; } } return method; } TypeMirror GetType(DbgEvaluationInfo evalInfo, DmdType type) => MonoDebugTypeCreator.GetType(this, type, CreateMonoTypeLoader(evalInfo)); internal Value CreateValueType(DbgEvaluationInfo evalInfo, DmdType type, int recursionCounter) { if (recursionCounter > 100) throw new InvalidOperationException(); if (!type.IsValueType) throw new InvalidOperationException(); var monoType = GetType(evalInfo, type); var fields = type.DeclaredFields; var monoFields = monoType.GetFields(); if (fields.Count != monoFields.Length) throw new InvalidOperationException(); var fieldValues = new List(monoFields.Length); for (int i = 0; i < monoFields.Length; i++) { Debug.Assert(fields[i].Name == monoFields[i].Name); var field = fields[i]; if (field.IsStatic || field.IsLiteral) continue; fieldValues.Add(CreateDefaultValue(evalInfo, field, 0)); } if (type.IsEnum) return monoType.VirtualMachine.CreateEnumMirror(monoType, (PrimitiveValue)fieldValues[0]); return monoType.VirtualMachine.CreateStructMirror(monoType, fieldValues.ToArray()); } Value CreateDefaultValue(DbgEvaluationInfo evalInfo, DmdFieldInfo field, int recursionCounter) { var type = field.FieldType; if (!type.IsValueType) return new PrimitiveValue(vm, ElementType.Object, null); if (type.IsPointer || type.IsFunctionPointer) return new PrimitiveValue(vm, ElementType.Ptr, 0L); if (!type.IsEnum) { switch (DmdType.GetTypeCode(type)) { case TypeCode.Boolean: return new PrimitiveValue(vm, ElementType.Boolean, false); case TypeCode.Char: return new PrimitiveValue(vm, ElementType.Char, '\0'); case TypeCode.SByte: return new PrimitiveValue(vm, ElementType.I1, (sbyte)0); case TypeCode.Byte: return new PrimitiveValue(vm, ElementType.U1, (byte)0); case TypeCode.Int16: return new PrimitiveValue(vm, ElementType.I2, (short)0); case TypeCode.UInt16: return new PrimitiveValue(vm, ElementType.U2, (ushort)0); case TypeCode.Int32: return new PrimitiveValue(vm, ElementType.I4, 0); case TypeCode.UInt32: return new PrimitiveValue(vm, ElementType.U4, 0U); case TypeCode.Int64: return new PrimitiveValue(vm, ElementType.I8, 0L); case TypeCode.UInt64: return new PrimitiveValue(vm, ElementType.U8, 0UL); case TypeCode.Single: return new PrimitiveValue(vm, ElementType.R4, 0f); case TypeCode.Double: return new PrimitiveValue(vm, ElementType.R8, 0d); } } return CreateValueType(evalInfo, type, recursionCounter + 1); } } readonly struct DbgCreateMonoValueResult { public Value? Value { get; } public string? ErrorMessage { get; } public DbgCreateMonoValueResult(Value value) { Value = value ?? throw new ArgumentNullException(nameof(value)); ErrorMessage = null; } public DbgCreateMonoValueResult(string errorMessage) { Value = null; ErrorMessage = errorMessage ?? throw new ArgumentNullException(nameof(errorMessage)); } } }