/*
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));
}
}
}