/* 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 dndbg.COM.CorDebug; using dndbg.Engine; using dnSpy.Contracts.Debugger; using dnSpy.Contracts.Debugger.DotNet.Evaluation; using dnSpy.Contracts.Debugger.Engine.Evaluation; using dnSpy.Contracts.Debugger.Evaluation; using dnSpy.Debugger.DotNet.CorDebug.CallStack; using dnSpy.Debugger.DotNet.CorDebug.Impl.Evaluation; using dnSpy.Debugger.DotNet.Metadata; namespace dnSpy.Debugger.DotNet.CorDebug.Impl { abstract partial class DbgEngineImpl { internal DbgDotNetValue CreateDotNetValue_CorDebug(CorValue value, DmdAppDomain reflectionAppDomain, bool tryCreateStrongHandle, bool closeOnContinue = true) { debuggerThread.VerifyAccess(); if (value is null) return new SyntheticValue(reflectionAppDomain.System_Void, new DbgDotNetRawValue(DbgSimpleValueType.Void)); try { var type = new ReflectionTypeCreator(this, reflectionAppDomain).Create(value.ExactType); if (tryCreateStrongHandle && !value.IsNull && !value.IsHandle && value.IsReference && !type.IsPointer && !type.IsFunctionPointer && !type.IsByRef) { var derefValue = value.GetDereferencedValue(out int hr); var strongHandle = derefValue?.CreateHandle(CorDebugHandleType.HANDLE_STRONG); Debug2.Assert(derefValue is null || strongHandle is not null || type == type.AppDomain.System_TypedReference); if (strongHandle is not null) value = strongHandle; } var dnValue = new DbgDotNetValueImpl(this, new DbgCorValueHolder(this, value, type)); if (closeOnContinue) { lock (lockObj) dotNetValuesToCloseOnContinue.Add(dnValue); } return dnValue; } catch { dnDebugger.DisposeHandle(value); throw; } } internal DbgDotNetValue CreateDotNetValue_CorDebug(DbgCorValueHolder value) { debuggerThread.VerifyAccess(); var dnValue = new DbgDotNetValueImpl(this, value); lock (lockObj) dotNetValuesToCloseOnContinue.Add(dnValue); return dnValue; } internal void Close(DbgCorValueHolder value) { if (CheckCorDebugThread()) { value.Dispose_CorDebug(); return; } bool start; lock (lockObj) { start = valuesToCloseNow.Count == 0; valuesToCloseNow.Add(value); } if (start) CorDebugThread(CloseValuesNow_CorDebug); } void CloseValuesNow_CorDebug() { debuggerThread.VerifyAccess(); DbgCorValueHolder[] values; lock (lockObj) { values = valuesToCloseNow.ToArray(); valuesToCloseNow.Clear(); } foreach (var value in values) value.Dispose_CorDebug(); } void CloseDotNetValues_CorDebug() { 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 DisposeHandle_CorDebug(CorValue? value) { Debug.Assert(debuggerThread.CheckAccess()); dnDebugger.DisposeHandle(value); } DbgDotNetRawValue? ReadField_CorDebug(CorValue? obj, DbgAppDomain? appDomain, string fieldName1, string fieldName2) { if (obj is null || appDomain is null) return null; var reflectionAppDomain = appDomain.GetReflectionAppDomain(); if (reflectionAppDomain is null) return null; DbgDotNetValueImpl? objImp = null; try { objImp = CreateDotNetValue_CorDebug(obj, reflectionAppDomain, tryCreateStrongHandle: false) as DbgDotNetValueImpl; if (objImp is null) return null; return ReadField_CorDebug(objImp, fieldName1, fieldName2); } finally { objImp?.Dispose(); } } DbgDotNetRawValue? ReadField_CorDebug(DbgDotNetValueImpl obj, string fieldName1, string? fieldName2) { const DmdBindingFlags fieldFlags = DmdBindingFlags.Public | DmdBindingFlags.NonPublic | DmdBindingFlags.Instance; var field = obj.Type.GetField(fieldName1, fieldFlags); if (field is null && fieldName2 is not null) field = obj.Type.GetField(fieldName2, fieldFlags); Debug2.Assert(field is not null); if (field is null) return null; var dnAppDomain = ((DbgCorDebugInternalAppDomainImpl)obj.Type.AppDomain.GetDebuggerAppDomain().InternalAppDomain).DnAppDomain; var corFieldDeclType = GetType(dnAppDomain.CorAppDomain, field.DeclaringType!); var objValue = DbgCorDebugInternalRuntimeImpl.TryGetObjectOrPrimitiveValue(obj.TryGetCorValue(), out int hr); if (objValue is null) return null; if (objValue.IsObject) { // This isn't a generic read-field method, so we won't try to load any classes by calling cctors. var fieldValue = objValue.GetFieldValue(corFieldDeclType.Class, (uint)field.MetadataToken, out hr); if (fieldValue is null) return null; DbgDotNetValue? dnValue = null; try { dnValue = CreateDotNetValue_CorDebug(fieldValue, field.AppDomain, tryCreateStrongHandle: false); return dnValue.GetRawValue(); } finally { dnValue?.Dispose(); } } return null; } CorType GetType(CorAppDomain appDomain, DmdType type) => CorDebugTypeCreator.GetType(this, appDomain, type); sealed class EvalTimedOut { } internal DbgDotNetValueResult? CheckFuncEval(DbgEvaluationInfo evalInfo) { debuggerThread.VerifyAccess(); if (dnDebugger.ProcessState != DebuggerProcessState.Paused) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CanFuncEvalOnlyWhenPaused); if (isUnhandledException) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEvalWhenUnhandledExceptionHasOccurred); if (evalInfo.Context.ContinueContext.HasData()) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOutNowDisabled); if (dnDebugger.IsEvaluating) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.CantFuncEval); return null; } internal DbgDotNetValueResult FuncEvalCall_CorDebug(DbgEvaluationInfo evalInfo, CorAppDomain appDomain, DmdMethodBase method, DbgDotNetValue? obj, object?[] arguments, bool newObj) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value; 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(CordbgErrorHelper.InternalError); var reflectionAppDomain = method.AppDomain; var methodDbgModule = method.Module.GetDebuggerModule() ?? throw new InvalidOperationException(); if (!TryGetDnModule(methodDbgModule, out var methodModule)) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); var func = methodModule.CorModule.GetFunctionFromToken((uint)method.MetadataToken) ?? throw new InvalidOperationException(); int hr; var dnThread = GetThread(evalInfo.Frame.Thread); var createdValues = new List(); try { using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); var converter = new EvalArgumentConverter(this, dnEval, appDomain, reflectionAppDomain, createdValues); var genTypeArgs = method.DeclaringType!.GetGenericArguments(); var methTypeArgs = method.GetGenericArguments(); var typeArgs = genTypeArgs.Count == 0 && methTypeArgs.Count == 0 ? Array.Empty() : new CorType[genTypeArgs.Count + methTypeArgs.Count]; int w = 0; for (int i = 0; i < genTypeArgs.Count; i++) typeArgs[w++] = GetType(appDomain, genTypeArgs[i]); for (int i = 0; i < methTypeArgs.Count; i++) typeArgs[w++] = GetType(appDomain, methTypeArgs[i]); if (typeArgs.Length != w) throw new InvalidOperationException(); var paramTypes = GetAllMethodParameterTypes(method.GetMethodSignature()); if (paramTypes.Count != arguments.Length) throw new InvalidOperationException(); bool hiddenThisArg = !method.IsStatic && !newObj; int argsCount = arguments.Length + (hiddenThisArg ? 1 : 0); var args = argsCount == 0 ? Array.Empty() : new CorValue[argsCount]; w = 0; DmdType origType; var declType = method.DeclaringType; if (hiddenThisArg) { 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); args[w++] = BoxIfNeeded(dnEval, appDomain, createdValues, val.CorValue!, declType, origType); } 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 ?? new ReflectionTypeCreator(this, method.AppDomain).Create(val.CorValue!.ExactType); args[w++] = BoxIfNeeded(dnEval, appDomain, createdValues, val.CorValue!, paramType, valType); } if (args.Length != w) throw new InvalidOperationException(); // Derefence/unbox the values here now that they can't get neutered for (int i = 0; i < args.Length; i++) { DmdType argType; if (!hiddenThisArg) argType = paramTypes[i]; else if (i == 0) argType = declType; else argType = paramTypes[i - 1]; CorValue? arg = args[i]; if (argType.IsValueType || argType.IsPointer || argType.IsFunctionPointer) { if (arg.IsReference) { if (arg.IsNull) throw new InvalidOperationException(); arg = arg.GetDereferencedValue(out hr); if (arg is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(hr)); } if (arg.IsBox) { arg = arg.GetBoxedValue(out hr); if (arg is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(hr)); } args[i] = arg; } } var res = newObj ? dnEval.CallConstructor(func, typeArgs, args, out hr) : dnEval.Call(func, typeArgs, args, out hr); if (res is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(hr)); if (res.Value.WasCustomNotification) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.FuncEvalRequiresAllThreadsToRun); if (res.Value.WasCancelled) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); if (res.Value.WasException) return DbgDotNetValueResult.CreateException(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, reflectionAppDomain, tryCreateStrongHandle: true)); return DbgDotNetValueResult.Create(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, reflectionAppDomain, tryCreateStrongHandle: true)); } } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); } finally { foreach (var value in createdValues) dnDebugger.DisposeHandle(value); } } CorValue BoxIfNeeded(DnEval dnEval, CorAppDomain appDomain, List createdValues, CorValue corValue, DmdType targetType, DmdType valueType) { if (!targetType.IsValueType && valueType.IsValueType && corValue.IsGeneric && !corValue.IsHeap) { var etype = corValue.ElementType; var corValueType = corValue.ExactType; if (corValueType?.HasClass == false) corValueType = GetType(appDomain, valueType); var boxedValue = dnEval.Box(corValue, corValueType) ?? throw new InvalidOperationException(); if (!boxedValue.Equals(corValue)) createdValues.Add(boxedValue); corValue = boxedValue; } return corValue; } 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_CorDebug(DbgEvaluationInfo evalInfo, CorAppDomain appDomain, CorValue value, DmdType type) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value; var dnThread = GetThread(evalInfo.Frame.Thread); var createdValues = new List(); CorValue? boxedValue = null; try { using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); boxedValue = BoxIfNeeded(dnEval, appDomain, createdValues, value, type.AppDomain.System_Object, type); if (boxedValue is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(-1)); return DbgDotNetValueResult.Create(CreateDotNetValue_CorDebug(boxedValue, type.AppDomain, tryCreateStrongHandle: true)); } } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); } finally { foreach (var v in createdValues) { if (!v.Equals(boxedValue)) dnDebugger.DisposeHandle(v); } } } internal DbgDotNetValueResult FuncEvalCreateInstanceNoCtor_CorDebug(DbgEvaluationInfo evalInfo, CorAppDomain appDomain, DmdType typeToCreate) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value; var dnThread = GetThread(evalInfo.Frame.Thread); try { using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); var corType = GetType(appDomain, typeToCreate); var res = dnEval.CreateDontCallConstructor(corType, out int hr); if (res is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(hr)); if (res.Value.WasCustomNotification) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.FuncEvalRequiresAllThreadsToRun); if (res.Value.WasCancelled) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); if (res.Value.WasException) return DbgDotNetValueResult.CreateException(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, typeToCreate.AppDomain, tryCreateStrongHandle: true)); return DbgDotNetValueResult.Create(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, typeToCreate.AppDomain, tryCreateStrongHandle: true)); } } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); } } void DnEval_EvalEvent(DnEval dnEval, DbgEvaluationInfo evalInfo) { if (dnEval.EvalTimedOut) evalInfo.Context.ContinueContext.GetOrCreateData(); dnDebugger.SignalEvalComplete(); } internal DbgDotNetValueResult CreateValue_CorDebug(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, object? value) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); if (value is DbgDotNetValueImpl) return DbgDotNetValueResult.Create((DbgDotNetValueImpl)value); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value; var dnThread = GetThread(evalInfo.Frame.Thread); var createdValues = new List(); CorValue? createdCorValue = null; try { var appDomain = ilFrame.GetCorAppDomain(); var reflectionAppDomain = ilFrame.GetReflectionModule().AppDomain; using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); var converter = new EvalArgumentConverter(this, dnEval, appDomain, reflectionAppDomain, createdValues); 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_CorDebug(evalRes.CorValue!, reflectionAppDomain, tryCreateStrongHandle: true); createdCorValue = evalRes.CorValue; return DbgDotNetValueResult.Create(resultValue); } } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); } finally { foreach (var v in createdValues) { if (!v.Equals(createdCorValue)) dnDebugger.DisposeHandle(v); } } } internal string? SetLocalValue_CorDebug(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, uint index, DmdType targetType, object? value) { Func createTargetValue = () => { var corValue = ilFrame.CorFrame.GetILLocal(index, out int hr); return new CreateCorValueResult(corValue, hr); }; return StoreValue_CorDebug(evalInfo, ilFrame, createTargetValue, targetType, value); } internal string? SetParameterValue_CorDebug(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, uint index, DmdType targetType, object? value) { Func createTargetValue = () => { var corValue = ilFrame.CorFrame.GetILArgument(index, out int hr); return new CreateCorValueResult(corValue, hr); }; return StoreValue_CorDebug(evalInfo, ilFrame, createTargetValue, targetType, value); } internal string? StoreValue_CorDebug(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, Func createTargetValue, DmdType targetType, object? sourceValue) { debuggerThread.VerifyAccess(); if (RequiresNoFuncEvalToStoreValue(targetType, sourceValue)) return StoreSimpleValue_CorDebug(evalInfo, ilFrame, createTargetValue, targetType, sourceValue); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value.ErrorMessage ?? throw new InvalidOperationException(); var dnThread = GetThread(evalInfo.Frame.Thread); var createdValues = new List(); CreateCorValueResult createResult = default; try { var appDomain = ilFrame.GetCorAppDomain(); var reflectionAppDomain = targetType.AppDomain; using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); var converter = new EvalArgumentConverter(this, dnEval, appDomain, reflectionAppDomain, createdValues); var evalRes = converter.Convert(sourceValue, targetType, out var newValueType); if (evalRes.ErrorMessage is not null) return evalRes.ErrorMessage; var sourceCorValue = evalRes.CorValue!; var sourceType = new ReflectionTypeCreator(this, reflectionAppDomain).Create(sourceCorValue.ExactType); createResult = createTargetValue(); if (createResult.Value is null) return CordbgErrorHelper.GetErrorMessage(createResult.HResult); return StoreValue_CorDebug(dnEval, createdValues, appDomain, dnThread, createResult.Value, targetType, sourceCorValue, sourceType); } } catch (TimeoutException) { return PredefinedEvaluationErrorMessages.FuncEvalTimedOut; } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return CordbgErrorHelper.InternalError; } finally { if (createResult.CanDispose) dnDebugger.DisposeHandle(createResult.Value); foreach (var v in createdValues) dnDebugger.DisposeHandle(v); } } string? StoreValue_CorDebug(DnEval dnEval, List createdValues, CorAppDomain appDomain, DnThread dnThread, CorValue targetValue, DmdType targetType, CorValue sourceValue, DmdType sourceType) { if (targetType.IsByRef) return CordbgErrorHelper.InternalError; int hr; if (!targetType.IsValueType) { if (!targetValue.IsReference) return CordbgErrorHelper.InternalError; if (!sourceValue.IsReference) { var boxedSourceValue = BoxIfNeeded(dnEval, appDomain, createdValues, sourceValue, targetType, sourceType); if (!boxedSourceValue.IsReference) return CordbgErrorHelper.InternalError; sourceValue = boxedSourceValue; } if (!sourceValue.IsNull && sourceType.IsValueType) { var sourceDerefVal = sourceValue.GetDereferencedValue(out hr); if (sourceDerefVal is null) return CordbgErrorHelper.GetErrorMessage(hr); if (!sourceDerefVal.IsBox) return CordbgErrorHelper.InternalError; } hr = targetValue.SetReferenceAddress(sourceValue.ReferenceAddress); if (hr != 0) return CordbgErrorHelper.GetErrorMessage(hr); return null; } else { if (!sourceType.IsValueType) return CordbgErrorHelper.InternalError; if (targetValue.IsReference) { if (!(targetValue.GetDereferencedValue(out hr) is CorValue derefValue)) return CordbgErrorHelper.GetErrorMessage(hr); targetValue = derefValue; } if (targetValue.IsBox) return CordbgErrorHelper.InternalError; if (sourceValue.IsReference) { if (!(sourceValue.GetDereferencedValue(out hr) is CorValue derefValue)) return CordbgErrorHelper.GetErrorMessage(hr); sourceValue = derefValue; } if (sourceValue.IsBox) { if (!(sourceValue.GetBoxedValue(out hr) is CorValue unboxedValue)) return CordbgErrorHelper.GetErrorMessage(hr); sourceValue = unboxedValue; } if (!targetValue.IsGeneric || !sourceValue.IsGeneric) return CordbgErrorHelper.InternalError; if (targetValue.Size != sourceValue.Size) return CordbgErrorHelper.InternalError; hr = targetValue.WriteGenericValue(sourceValue.ReadGenericValue(), dnThread.Process.CorProcess); if (hr < 0) return CordbgErrorHelper.GetErrorMessage(hr); return null; } } string? StoreSimpleValue_CorDebug(DbgEvaluationInfo evalInfo, ILDbgEngineStackFrame ilFrame, Func createTargetValue, DmdType targetType, object? sourceValue) { Debug.Assert(RequiresNoFuncEvalToStoreValue(targetType, sourceValue)); evalInfo.CancellationToken.ThrowIfCancellationRequested(); CreateCorValueResult createResult = default; try { var dnThread = GetThread(evalInfo.Frame.Thread); createResult = createTargetValue(); if (createResult.Value is null) return CordbgErrorHelper.GetErrorMessage(createResult.HResult); return StoreSimpleValue_CorDebug(dnThread, createResult.Value, targetType, sourceValue); } catch (TimeoutException) { return PredefinedEvaluationErrorMessages.FuncEvalTimedOut; } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return CordbgErrorHelper.InternalError; } finally { if (createResult.CanDispose) dnDebugger.DisposeHandle(createResult.Value); } } static bool IsNoFuncEvalValue(object? value) { if (value is null) return true; var type = value.GetType(); var tc = Type.GetTypeCode(type); if (TypeCode.Boolean <= tc && tc <= TypeCode.Double) return true; if (type == typeof(IntPtr) || type == typeof(UIntPtr)) return true; return false; } static bool RequiresNoFuncEvalToStoreValue(DmdType targetType, object? sourceValue) { // Boxing requires func-eval if (!(targetType.IsValueType || targetType.IsPointer || targetType.IsFunctionPointer) && sourceValue is not null) return false; // Only primitive value types are supported if (!IsNoFuncEvalValue(sourceValue)) return false; return true; } string? StoreSimpleValue_CorDebug(DnThread dnThread, CorValue targetValue, DmdType targetType, object? sourceValue) { if (targetType.IsByRef) return CordbgErrorHelper.InternalError; int hr; if (targetType.IsPointer || targetType.IsFunctionPointer) { var sourceValueBytes = TryGetValueBytes(sourceValue); Debug2.Assert(sourceValueBytes is not null); if (sourceValueBytes is null || targetValue.Size != (uint)sourceValueBytes.Length) return CordbgErrorHelper.InternalError; ulong address = targetValue.Address; if (address == 0) return CordbgErrorHelper.InternalError; hr = dnThread.Process.CorProcess.WriteMemory(address, sourceValueBytes, 0, sourceValueBytes.Length, out var sizeWritten); if (hr < 0) return CordbgErrorHelper.GetErrorMessage(hr); return null; } else if (!targetType.IsValueType) { if (sourceValue is not null) return CordbgErrorHelper.InternalError; hr = targetValue.SetReferenceAddress(0); if (hr != 0) return CordbgErrorHelper.GetErrorMessage(hr); return null; } else { if (targetValue.IsReference) { if (!(targetValue.GetDereferencedValue(out hr) is CorValue derefValue)) return CordbgErrorHelper.GetErrorMessage(hr); targetValue = derefValue; } if (targetValue.IsBox) return CordbgErrorHelper.InternalError; if (!targetValue.IsGeneric || sourceValue is null) return CordbgErrorHelper.InternalError; var sourceValueBytes = TryGetValueBytes(sourceValue); Debug2.Assert(sourceValueBytes is not null); if (sourceValueBytes is null || targetValue.Size != (uint)sourceValueBytes.Length) return CordbgErrorHelper.InternalError; hr = targetValue.WriteGenericValue(sourceValueBytes, dnThread.Process.CorProcess); if (hr < 0) return CordbgErrorHelper.GetErrorMessage(hr); return null; } } static byte[]? TryGetValueBytes(object? value) { if (value is null) return null; var type = value.GetType(); switch (Type.GetTypeCode(type)) { case TypeCode.Boolean: return new byte[1] { (byte)((bool)value ? 1 : 0) }; case TypeCode.Char: return BitConverter.GetBytes((char)value); case TypeCode.SByte: return new byte[1] { (byte)(sbyte)value }; case TypeCode.Byte: return new byte[1] { (byte)value }; case TypeCode.Int16: return BitConverter.GetBytes((short)value); case TypeCode.UInt16: return BitConverter.GetBytes((ushort)value); case TypeCode.Int32: return BitConverter.GetBytes((int)value); case TypeCode.UInt32: return BitConverter.GetBytes((uint)value); case TypeCode.Int64: return BitConverter.GetBytes((long)value); case TypeCode.UInt64: return BitConverter.GetBytes((ulong)value); case TypeCode.Single: return BitConverter.GetBytes((float)value); case TypeCode.Double: return BitConverter.GetBytes((double)value); } if (type == typeof(IntPtr)) { if (IntPtr.Size == 4) return BitConverter.GetBytes(((IntPtr)value).ToInt32()); return BitConverter.GetBytes(((IntPtr)value).ToInt64()); } if (type == typeof(UIntPtr)) { if (UIntPtr.Size == 4) return BitConverter.GetBytes(((UIntPtr)value).ToUInt32()); return BitConverter.GetBytes(((UIntPtr)value).ToUInt64()); } return null; } // This method calls ICorDebugEval2.NewParameterizedArray() which doesn't support creating SZ arrays // with any element type. See the caller of this method (CreateSZArrayCore) for more info. internal DbgDotNetValueResult CreateSZArray_CorDebug(DbgEvaluationInfo evalInfo, CorAppDomain appDomain, DmdType elementType, int length) { debuggerThread.VerifyAccess(); evalInfo.CancellationToken.ThrowIfCancellationRequested(); var tmp = CheckFuncEval(evalInfo); if (tmp is not null) return tmp.Value; var dnThread = GetThread(evalInfo.Frame.Thread); try { using (var dnEval = dnDebugger.CreateEval(evalInfo.CancellationToken, suspendOtherThreads: (evalInfo.Context.Options & DbgEvaluationContextOptions.RunAllThreads) == 0)) { dnEval.SetThread(dnThread); dnEval.SetTimeout(evalInfo.Context.FuncEvalTimeout); dnEval.EvalEvent += (s, e) => DnEval_EvalEvent(dnEval, evalInfo); var corType = GetType(appDomain, elementType); var res = dnEval.CreateSZArray(corType, length, out int hr); if (res is null) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.GetErrorMessage(hr)); Debug.Assert(!res.Value.WasException, "Shouldn't throw " + nameof(ArgumentOutOfRangeException)); if (res.Value.WasCustomNotification) return DbgDotNetValueResult.CreateError(CordbgErrorHelper.FuncEvalRequiresAllThreadsToRun); if (res.Value.WasCancelled) return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); if (res.Value.WasException) return DbgDotNetValueResult.CreateException(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, elementType.AppDomain, tryCreateStrongHandle: true)); return DbgDotNetValueResult.Create(CreateDotNetValue_CorDebug(res.Value.ResultOrException!, elementType.AppDomain, tryCreateStrongHandle: true)); } } catch (TimeoutException) { return DbgDotNetValueResult.CreateError(PredefinedEvaluationErrorMessages.FuncEvalTimedOut); } catch (Exception ex) when (ExceptionUtils.IsInternalDebuggerError(ex)) { return DbgDotNetValueResult.CreateError(CordbgErrorHelper.InternalError); } } internal DbgDotNetReturnValueInfo[] GetCurrentReturnValues() { debuggerThread.VerifyAccess(); var currentReturnValues = this.currentReturnValues; if (currentReturnValues.Length == 0) return Array.Empty(); var res = new DbgDotNetReturnValueInfo[currentReturnValues.Length]; try { for (int i = 0; i < res.Length; i++) { var info = currentReturnValues[i]; var dnValue = ((DbgDotNetValueImpl)info.Value).CorValueHolder.AddRef(); try { res[i] = new DbgDotNetReturnValueInfo(info.Id, info.Method, CreateDotNetValue_CorDebug(dnValue)); } catch { dnValue.Release(); throw; } } return res; } catch { foreach (var info in res) info.Value?.Dispose(); throw; } } internal DbgDotNetValue? GetCurrentReturnValue(uint id) { debuggerThread.VerifyAccess(); var currentReturnValues = this.currentReturnValues; if (id == DbgDotNetRuntimeConstants.LastReturnValueId) id = (uint)currentReturnValues.Length; int index = (int)id - 1; if ((uint)index >= (uint)currentReturnValues.Length) return null; var info = currentReturnValues[index]; var dnValue = ((DbgDotNetValueImpl)info.Value).CorValueHolder.AddRef(); try { return CreateDotNetValue_CorDebug(dnValue); } catch { dnValue.Release(); throw; } } internal void SetReturnValues(DbgDotNetReturnValueInfo[] returnValues) { debuggerThread.VerifyAccess(); for (int i = 0; i < returnValues.Length; i++) { if (returnValues[i].Id != (uint)i + 1) throw new ArgumentException(); } foreach (var info in currentReturnValues) info.Value.Dispose(); currentReturnValues = returnValues; } DbgDotNetReturnValueInfo[] currentReturnValues; } }