tonerdo / pose

Replace any .NET method (including static and non-virtual) with a delegate
MIT License
1.07k stars 75 forks source link

Dose not work net core 2.1 #41

Open LucaGabi opened 5 years ago

LucaGabi commented 5 years ago
Shim classShim = Shim.Replace(() => Console.WriteLine(Is.A<string>())).With(
                    delegate (string s) {
                        Console.WriteLine("here");
                    });

Console.WriteLine("test");
dennisholmer commented 5 years ago

Confirmed, this does not work either: Shim.Replace(() => TimeZoneInfo.Local).With(() => TimeZoneInfo.Utc);

airbreather commented 5 years ago

It's not completely broken in .NET Core 2.1, but there are some really interesting things specific to the low-level CLR magic that .NET Core has subscribed to, which I think Pose would have to work around in order to keep working the way it does in a ton of cases that don't even appear to be all that complex (on the surface).

I'm not in any way affiliated with this project (it just got linked from a Reddit thread, and I was intrigued), but I think the basic idea would be to maintain a list of methods that are implemented as JIT intrinsics and skip rewriting them unless the user explicitly provided an override.

The below code throws an InvalidProgramException, which other threads seem to have touched on, but that's not actually the interesting part of it. I've included a Ben.Demystifier-processed stack trace so you can follow along (all links go to dotnet/coreclr as of tag "v2.1.11", in case you want to browse around it more):

Here's the source code so you can mess around with it:

ConsoleApp0.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Pose" Version="1.2.1" />
    <PackageReference Include="Ben.Demystifier" Version="0.1.4" />
  </ItemGroup>

</Project>

global.json

{
  "sdk": {
    "version": "2.1.604"
  }
}

Program.cs

using System;
using System.Diagnostics;
using System.Runtime.ExceptionServices;
using Pose;

class Program
{
    static void Main()
    {
        try
        {
            // writes my actual local offset:
            Console.WriteLine(TimeZoneInfo.Local.BaseUtcOffset);

            TimeSpan overriddenOffset = default;
            PoseContext.Isolate(() => overriddenOffset = TimeZoneInfo.Local.BaseUtcOffset,
                                Shim.Replace(() => TimeZoneInfo.Local).With(() => TimeZoneInfo.Utc));

            // writes "00:00:00":
            Console.WriteLine(overriddenOffset);

            // writes my actual local offset again:
            Console.WriteLine(TimeZoneInfo.Local.BaseUtcOffset);

            // throws an error with the stack trace I'm going to paste:
            PoseContext.Isolate(() => Console.WriteLine(TimeZoneInfo.Local.BaseUtcOffset),
                                Shim.Replace(() => TimeZoneInfo.Local).With(() => TimeZoneInfo.Utc));
        }
        catch (Exception ex)
        {
            ExceptionDispatchInfo.Capture(ex.Demystify()).Throw();
        }
    }
}

Stack Trace:

Unhandled Exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidProgramException: Common Language Runtime detected an invalid program.
   at int dynamic_System.Collections.Generic.GenericEqualityComparer`1[System.String]_LastIndexOf(GenericEqualityComparer<string>, string[], string, int, int)
   at int stub_virt_System.Collections.Generic.EqualityComparer`1[System.String]_LastIndexOf(EqualityComparer<string>, string[], string, int, int, RuntimeMethodHandle, RuntimeTypeHandle)
   at int dynamic_System.Array_LastIndexOf(string[], string, int, int)
   at int stub_System.Array_LastIndexOf(string[], string, int, int, RuntimeMethodHandle, RuntimeTypeHandle)
   at int dynamic_System.Collections.Generic.List`1[System.String]_LastIndexOf(List<string>, string, int, int)
   at int stub_System.Collections.Generic.List`1[System.String]_LastIndexOf(List<string>, string, int, int, RuntimeMethodHandle, RuntimeTypeHandle)
   at int dynamic_System.Collections.Generic.List`1[System.String]_LastIndexOf(List<string>, string)
   at int stub_virt_System.Collections.Generic.List`1[System.String]_LastIndexOf(List<string>, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.SR_InternalGetResourceString(string)
   at string stub_System.SR_InternalGetResourceString(string, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.SR_GetResourceString(string, string)
   at string stub_System.SR_GetResourceString(string, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at string stub_System.SR_get_Arg_PlatformNotSupported(RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.PlatformNotSupportedException_.ctor(PlatformNotSupportedException)
   at PlatformNotSupportedException stub_ctor_System.PlatformNotSupportedException_.ctor(RuntimeMethodHandle, RuntimeTypeHandle)
   at Byte& dynamic_Internal.Runtime.CompilerServices.Unsafe_As(ref char)
   at Byte& stub_Internal.Runtime.CompilerServices.Unsafe_As(ref char, RuntimeMethodHandle, RuntimeTypeHandle)
   at bool dynamic_System.String_EqualsHelper(string, string)
   at bool stub_System.String_EqualsHelper(string, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at bool stub_System.String_Equals(string, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at bool stub_System.String_op_Equality(string, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at ResourceSet dynamic_System.Resources.ResourceManager_GetFirstResourceSet(ResourceManager, CultureInfo)
   at ResourceSet stub_System.Resources.ResourceManager_GetFirstResourceSet(ResourceManager, CultureInfo, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.Resources.ResourceManager_GetString(ResourceManager, string, CultureInfo)
   at string stub_virt_System.Resources.ResourceManager_GetString(ResourceManager, string, CultureInfo, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.SR_InternalGetResourceString(string)
   at string stub_System.SR_InternalGetResourceString(string, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.SR_GetResourceString(string, string)
   at string stub_System.SR_GetResourceString(string, string, RuntimeMethodHandle, RuntimeTypeHandle)
   at string stub_System.SR_get_Arg_PlatformNotSupported(RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.PlatformNotSupportedException_.ctor(PlatformNotSupportedException)
   at PlatformNotSupportedException stub_ctor_System.PlatformNotSupportedException_.ctor(RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.ByReference`1[System.Char]_.ctor(ref ByReference<char>, ref char)
   at ByReference<char> stub_ctor_System.ByReference`1[System.Char]_.ctor(ref char, RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.ReadOnlySpan`1[System.Char]_.ctor(ref ReadOnlySpan<char>, ref char, int)
   at ReadOnlySpan<char> stub_ctor_System.ReadOnlySpan`1[System.Char]_.ctor(ref char, int, RuntimeMethodHandle, RuntimeTypeHandle)
   at ReadOnlySpan<char> dynamic_System.String_op_Implicit(string)
   at ReadOnlySpan<char> stub_System.String_op_Implicit(string, RuntimeMethodHandle, RuntimeTypeHandle)
   at StringBuilder dynamic_System.Globalization.TimeSpanFormat_FormatToBuilder(TimeSpan, ReadOnlySpan<char>, IFormatProvider)
   at StringBuilder stub_System.Globalization.TimeSpanFormat_FormatToBuilder(TimeSpan, ReadOnlySpan<char>, IFormatProvider, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.Globalization.TimeSpanFormat_Format(TimeSpan, string, IFormatProvider)
   at string stub_System.Globalization.TimeSpanFormat_Format(TimeSpan, string, IFormatProvider, RuntimeMethodHandle, RuntimeTypeHandle)
   at string dynamic_System.TimeSpan_ToString(ref TimeSpan, string, IFormatProvider)
   at string stub_virt_System.IFormattable_ToString(IFormattable, string, IFormatProvider, RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.IO.TextWriter_WriteLine(TextWriter, object)
   at void stub_virt_System.IO.TextWriter_WriteLine(TextWriter, object, RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_System.IO.SyncTextWriter_WriteLine(SyncTextWriter, object)
   at void stub_virt_System.IO.TextWriter_WriteLine(TextWriter, object, RuntimeMethodHandle, RuntimeTypeHandle)
   at void stub_System.Console_WriteLine(object, RuntimeMethodHandle, RuntimeTypeHandle)
   at void dynamic_Program+<>c_<Main>b__0_3(?)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at Pose.PoseContext.Isolate(Action entryPoint, Shim[] shims)
   at Program.Main() in C:\<SNIP>\Program.cs:line 26
--- End of stack trace from previous location where exception was thrown ---
   at Program.Main() in C:\<SNIP>\Program.cs:line 31