public void TestDMD() { Counter = 0; // Run the original method. Assert.Equal(Tuple.Create(StringOriginal, 1), ExampleMethod(1)); MethodInfo original = typeof(DynamicMethodDefinitionTest).GetMethod(nameof(ExampleMethod)); MethodBase patched; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(original)) { Assert.Equal("i", dmd.Definition.Parameters[0].Name); // Modify the MethodDefinition. foreach (Instruction instr in dmd.Definition.Body.Instructions) { if (instr.Operand as string == StringOriginal) { instr.Operand = StringPatched; } } // Generate a DynamicMethod from the modified MethodDefinition. patched = dmd.Generate(); } using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(ExampleGenericClass <int>).GetMethod(nameof(ExampleMethod)))) { Assert.Equal(0, ((Func <int>)dmd.Generate().CreateDelegate(typeof(Func <int>)))()); Assert.Equal(0, ((Func <int>)DMDCecilGenerator.Generate(dmd).CreateDelegate(typeof(Func <int>)))()); } // Run the DynamicMethod. Assert.Equal(Tuple.Create(StringPatched, 3), patched.Invoke(null, new object[] { 2 })); // Detour the original method to the patched DynamicMethod, then run the patched method. using (new Detour(original, patched)) { // The detour is only active in this block. Assert.Equal(Tuple.Create(StringPatched, 6), ExampleMethod(3)); } // Run the original method again. Assert.Equal(Tuple.Create(StringOriginal, 10), ExampleMethod(4)); }
public void TestDMD() { Counter = 0; // Run the original method. Assert.Equal(Tuple.Create(StringOriginal, 1), ExampleMethod(1)); MethodInfo original = typeof(DynamicMethodDefinitionTest).GetMethod(nameof(ExampleMethod)); MethodBase patched; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(original)) { Assert.Equal("i", dmd.Definition.Parameters[0].Name); // Modify the MethodDefinition. foreach (Instruction instr in dmd.Definition.Body.Instructions) { if (instr.Operand as string == StringOriginal) { instr.Operand = StringPatched; } } // Generate a DynamicMethod from the modified MethodDefinition. patched = dmd.Generate(); } using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(ExampleGenericClass <int>).GetMethod(nameof(ExampleMethod)))) { Assert.Equal(0, ((Func <int>)dmd.Generate().CreateDelegate(typeof(Func <int>)))()); Assert.Equal(0, ((Func <int>)DMDCecilGenerator.Generate(dmd).CreateDelegate(typeof(Func <int>)))()); Assert.Equal(dmd.Name = "SomeManualDMDName", dmd.Generate().Name); Counter -= 2; // This was indirectly provided by Pathoschild (SMAPI). // Microsoft.GeneratedCode can be loaded multiple times and have different contents. // This tries to recreate that scenario... and this is the best place to test it at the time of writing. #if NETFRAMEWORK && true AssemblyBuilder abDupeA = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName() { Name = "MonoMod.UnitTest.AssemblyDupe" }, AssemblyBuilderAccess.RunAndSave ); ModuleBuilder mbDupeA = abDupeA.DefineDynamicModule($"{abDupeA.GetName().Name}.dll"); TypeBuilder tbDupeA = mbDupeA.DefineType( "DupeA", System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Class ); Type tDupeA = tbDupeA.CreateType(); AssemblyBuilder abDupeB = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName() { Name = abDupeA.GetName().Name }, AssemblyBuilderAccess.RunAndSave ); ModuleBuilder mbDupeB = abDupeB.DefineDynamicModule($"{abDupeB.GetName().Name}.dll"); TypeBuilder tbDupeB = mbDupeB.DefineType( "DupeB", System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Class ); Type tDupeB = tbDupeB.CreateType(); Assert.Equal(tDupeA.Assembly.FullName, tDupeB.Assembly.FullName); Assert.NotEqual(tDupeA.Assembly, tDupeB.Assembly); TypeReference trDupeA = dmd.Module.ImportReference(tDupeA); TypeReference trDupeB = dmd.Module.ImportReference(tDupeB); Assert.Equal(trDupeA.Scope.Name, trDupeB.Scope.Name); // "Fun" fact: both share matching AssemblyNames, so the first scope gets reused! // Assert.NotEqual(trDupeA.Scope, trDupeB.Scope); Assert.Equal(tDupeA, trDupeA.ResolveReflection()); Assert.Equal(tDupeB, trDupeB.ResolveReflection()); #endif } // Run the DynamicMethod. Assert.Equal(Tuple.Create(StringPatched, 3), patched.Invoke(null, new object[] { 2 })); // Detour the original method to the patched DynamicMethod, then run the patched method. using (new Detour(original, patched)) { // The detour is only active in this block. Assert.Equal(Tuple.Create(StringPatched, 6), ExampleMethod(3)); } // Run the original method again. Assert.Equal(Tuple.Create(StringOriginal, 10), ExampleMethod(4)); }
public void TestDMD() { Counter = 0; // Run the original method. Assert.Equal(Tuple.Create(StringOriginal, 1), ExampleMethod(1)); MethodInfo original = typeof(DynamicMethodDefinitionTest).GetMethod(nameof(ExampleMethod)); MethodBase patched; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(original)) { Assert.Equal("i", dmd.Definition.Parameters[0].Name); // Modify the MethodDefinition. foreach (Instruction instr in dmd.Definition.Body.Instructions) { if (instr.Operand as string == StringOriginal) { instr.Operand = StringPatched; } else if (instr.MatchCallOrCallvirt <DynamicMethodDefinitionTest>(nameof(ExampleMethod))) { instr.Operand = dmd.Definition; } } // Generate a DynamicMethod from the modified MethodDefinition. patched = dmd.Generate(); } // Generate an entirely new method that just returns a stack trace for further testing. DynamicMethod stacker; using (DynamicMethodDefinition dmd = new DynamicMethodDefinition("Stacker", typeof(StackTrace), new Type[0])) { using (ILContext il = new ILContext(dmd.Definition)) il.Invoke(_ => { ILCursor c = new ILCursor(il); for (int i = 0; i < 32; i++) { c.Emit(OpCodes.Nop); } c.Emit(OpCodes.Newobj, typeof(StackTrace).GetConstructor(new Type[0])); for (int i = 0; i < 32; i++) { c.Emit(OpCodes.Nop); } c.Emit(OpCodes.Ret); }); stacker = (DynamicMethod)DMDEmitDynamicMethodGenerator.Generate(dmd, null); } using (DynamicMethodDefinition dmd = new DynamicMethodDefinition(typeof(ExampleGenericClass <int>).GetMethod(nameof(ExampleMethod)))) { Assert.Equal(0, ((Func <int>)dmd.Generate().CreateDelegate(typeof(Func <int>)))()); Assert.Equal(0, ((Func <int>)DMDCecilGenerator.Generate(dmd).CreateDelegate(typeof(Func <int>)))()); Assert.Equal(dmd.Name = "SomeManualDMDName", dmd.Generate().Name); Counter -= 2; // This was indirectly provided by Pathoschild (SMAPI). // Microsoft.GeneratedCode can be loaded multiple times and have different contents. // This tries to recreate that scenario... and this is the best place to test it at the time of writing. #if NETFRAMEWORK && true AssemblyBuilder abDupeA = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName() { Name = "MonoMod.UnitTest.AssemblyDupe" }, AssemblyBuilderAccess.RunAndSave ); ModuleBuilder mbDupeA = abDupeA.DefineDynamicModule($"{abDupeA.GetName().Name}.dll"); TypeBuilder tbDupeA = mbDupeA.DefineType( "DupeA", System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Class ); Type tDupeA = tbDupeA.CreateType(); AssemblyBuilder abDupeB = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName() { Name = abDupeA.GetName().Name }, AssemblyBuilderAccess.RunAndSave ); ModuleBuilder mbDupeB = abDupeB.DefineDynamicModule($"{abDupeB.GetName().Name}.dll"); TypeBuilder tbDupeB = mbDupeB.DefineType( "DupeB", System.Reflection.TypeAttributes.Public | System.Reflection.TypeAttributes.Abstract | System.Reflection.TypeAttributes.Sealed | System.Reflection.TypeAttributes.Class ); Type tDupeB = tbDupeB.CreateType(); Assert.Equal(tDupeA.Assembly.FullName, tDupeB.Assembly.FullName); Assert.NotEqual(tDupeA.Assembly, tDupeB.Assembly); TypeReference trDupeA = dmd.Module.ImportReference(tDupeA); TypeReference trDupeB = dmd.Module.ImportReference(tDupeB); Assert.Equal(trDupeA.Scope.Name, trDupeB.Scope.Name); // "Fun" fact: both share matching AssemblyNames, so the first scope gets reused! // Assert.NotEqual(trDupeA.Scope, trDupeB.Scope); Assert.Equal(tDupeA, trDupeA.ResolveReflection()); Assert.Equal(tDupeB, trDupeB.ResolveReflection()); #endif } // Run the DynamicMethod. Assert.Equal(Tuple.Create(StringPatched, 3), patched.Invoke(null, new object[] { 2 })); Assert.Equal(Tuple.Create(StringPatched, 3), patched.Invoke(null, new object[] { 1337 })); // Detour the original method to the patched DynamicMethod, then run the patched method. using (new Detour(original, patched)) { // The detour is only active in this block. Assert.Equal(Tuple.Create(StringPatched, 6), ExampleMethod(3)); } // Run the original method again. Assert.Equal(Tuple.Create(StringOriginal, 10), ExampleMethod(4)); // Verify that we can still obtain the real DynamicMethod. // .NET uses a wrapping RTDynamicMethod to avoid leaking the mutable DynamicMethod. // Mono uses RuntimeMethodInfo without any link to the original DynamicMethod. if (Type.GetType("Mono.Runtime") != null) { stacker.Pin(); } StackTrace stack = ((Func <StackTrace>)stacker.CreateDelegate(typeof(Func <StackTrace>)))(); MethodBase stacked = stack.GetFrames().First(f => f.GetMethod()?.IsDynamicMethod() ?? false).GetMethod(); Assert.NotEqual(stacker, stacked); Assert.Equal(stacker, stacked.GetIdentifiable()); Assert.Equal(stacker.GetNativeStart(), stacked.GetNativeStart()); // This will always be true on .NET and only be true on Mono if the method is still pinned. Assert.IsAssignableFrom <DynamicMethod>(stacked.GetIdentifiable()); if (Type.GetType("Mono.Runtime") != null) { stacker.Unpin(); } }