Skip to content

Extends Verify to allow verification of assemblies via ICSharpCode.Decompiler

License

Notifications You must be signed in to change notification settings

VerifyTests/Verify.ICSharpCode.Decompiler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Verify.ICSharpCode.Decompiler

Discussions Build status NuGet Status

Extends Verify to allow verification of assemblies via ICSharpCode.Decompiler.

See Milestones for release notes.

NuGet package

https://nuget.org/packages/Verify.ICSharpCode.Decompiler/

Usage

[ModuleInitializer]
public static void Init() =>
    VerifyICSharpCodeDecompiler.Initialize();

snippet source | anchor

Then given the following type:

using System.ComponentModel;

public class Target :
    INotifyPropertyChanged
{
    void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
        PropertyChanged?.Invoke(this, new(propertyName));

    public event PropertyChangedEventHandler? PropertyChanged;

    string? property;

    public string? Property
    {
        get => property;
        set
        {
            property = value;
            OnPropertyChanged();
        }
    }
}

snippet source | anchor

Verify Type

[Test]
public async Task TypeDefinitionUsage()
{
    using var file = new PEFile(assemblyPath);
    var type = file.Metadata.TypeDefinitions
        .Single(
            _ =>
            {
                var fullName = _.GetFullTypeName(file.Metadata);
                return fullName.Name == "Target";
            });
    await Verify(new TypeToDisassemble(file, type));
}

snippet source | anchor

Result:

.class public auto ansi beforefieldinit Target
	extends [System.Runtime]System.Object
	implements [System.ObjectModel]System.ComponentModel.INotifyPropertyChanged
{
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
		01 00 00 00 00
	)
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
		01 00 02 00 00
	)
	.interfaceimpl type [System.ObjectModel]System.ComponentModel.INotifyPropertyChanged
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
		01 00 00 00 00
	)

	// Fields
	.field private string 'property'
	.field private class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler PropertyChanged
	.custom instance void [System.Runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggerBrowsableState) = (
		01 00 00 00 00 00 00 00
	)
	.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
		01 00 00 00
	)

	// Methods
	.method public hidebysig specialname rtspecialname 
		instance void .ctor () cil managed 
	{
		// Header size: 1
		// Code size: 8 (0x8)
		.maxstack 8

		IL_0000: ldarg.0
		IL_0001: call instance void [System.Runtime]System.Object::.ctor()
		IL_0006: nop
		IL_0007: ret
	} // end of method Target::.ctor

	.method public final hidebysig specialname newslot virtual 
		instance void add_PropertyChanged (
			class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler 'value'
		) cil managed 
	{
		.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
			01 00 00 00
		)
		// Header size: 12
		// Code size: 41 (0x29)
		.maxstack 3
		.locals init (
			[0] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
			[1] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
			[2] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
		)

		IL_0000: ldarg.0
		IL_0001: ldfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
		IL_0006: stloc.0
		// loop start (head: IL_0007)
			IL_0007: ldloc.0
			IL_0008: stloc.1
			IL_0009: ldloc.1
			IL_000a: ldarg.1
			IL_000b: call class [System.Runtime]System.Delegate [System.Runtime]System.Delegate::Combine(class [System.Runtime]System.Delegate, class [System.Runtime]System.Delegate)
			IL_0010: castclass [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
			IL_0015: stloc.2
			IL_0016: ldarg.0
			IL_0017: ldflda class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
			IL_001c: ldloc.2
			IL_001d: ldloc.1
			IL_001e: call !!0 [System.Threading]System.Threading.Interlocked::CompareExchange<class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler>(!!0&, !!0, !!0)
			IL_0023: stloc.0
			IL_0024: ldloc.0
			IL_0025: ldloc.1
			IL_0026: bne.un.s IL_0007
		// end loop
		IL_0028: ret
	} // end of method Target::add_PropertyChanged

	.method public hidebysig specialname 
		instance string get_Property () cil managed 
	{
		// Header size: 1
		// Code size: 7 (0x7)
		.maxstack 8

		IL_0000: ldarg.0
		IL_0001: ldfld string Target::'property'
		IL_0006: ret
	} // end of method Target::get_Property

	.method private hidebysig 
		instance void OnPropertyChanged (
			[opt] string propertyName
		) cil managed 
	{
		.param [1] = nullref
			.custom instance void [System.Runtime]System.Runtime.CompilerServices.CallerMemberNameAttribute::.ctor() = (
				01 00 00 00
			)
		// Header size: 1
		// Code size: 26 (0x1a)
		.maxstack 8

		IL_0000: ldarg.0
		IL_0001: ldfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
		IL_0006: dup
		IL_0007: brtrue.s IL_000c

		IL_0009: pop
		IL_000a: br.s IL_0019

		IL_000c: ldarg.0
		IL_000d: ldarg.1
		IL_000e: newobj instance void [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs::.ctor(string)
		IL_0013: callvirt instance void [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
		IL_0018: nop

		IL_0019: ret
	} // end of method Target::OnPropertyChanged

	.method public final hidebysig specialname newslot virtual 
		instance void remove_PropertyChanged (
			class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler 'value'
		) cil managed 
	{
		.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
			01 00 00 00
		)
		// Header size: 12
		// Code size: 41 (0x29)
		.maxstack 3
		.locals init (
			[0] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
			[1] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler,
			[2] class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
		)

		IL_0000: ldarg.0
		IL_0001: ldfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
		IL_0006: stloc.0
		// loop start (head: IL_0007)
			IL_0007: ldloc.0
			IL_0008: stloc.1
			IL_0009: ldloc.1
			IL_000a: ldarg.1
			IL_000b: call class [System.Runtime]System.Delegate [System.Runtime]System.Delegate::Remove(class [System.Runtime]System.Delegate, class [System.Runtime]System.Delegate)
			IL_0010: castclass [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler
			IL_0015: stloc.2
			IL_0016: ldarg.0
			IL_0017: ldflda class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
			IL_001c: ldloc.2
			IL_001d: ldloc.1
			IL_001e: call !!0 [System.Threading]System.Threading.Interlocked::CompareExchange<class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler>(!!0&, !!0, !!0)
			IL_0023: stloc.0
			IL_0024: ldloc.0
			IL_0025: ldloc.1
			IL_0026: bne.un.s IL_0007
		// end loop
		IL_0028: ret
	} // end of method Target::remove_PropertyChanged

	.method public hidebysig specialname 
		instance void set_Property (
			string 'value'
		) cil managed 
	{
		// Header size: 1
		// Code size: 21 (0x15)
		.maxstack 8

		IL_0000: nop
		IL_0001: ldarg.0
		IL_0002: ldarg.1
		IL_0003: stfld string Target::'property'
		IL_0008: ldarg.0
		IL_0009: ldstr "Property"
		IL_000e: call instance void Target::OnPropertyChanged(string)
		IL_0013: nop
		IL_0014: ret
	} // end of method Target::set_Property

	// Events
	.event [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler PropertyChanged
	{
		.addon instance void Target::add_PropertyChanged(class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler)
		.removeon instance void Target::remove_PropertyChanged(class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler)
	}


	// Properties
	.property instance string Property()
	{
		.get instance string Target::get_Property()
		.set instance void Target::set_Property(string)
	}

} // end of class Target

snippet source | anchor

A string for the type name can also be used:

[Test]
public async Task TypeNameUsage()
{
    using var file = new PEFile(assemblyPath);
    await Verify(new TypeToDisassemble(file, "Target"));
}

snippet source | anchor

Verify Method

[Test]
public async Task MethodNameUsage()
{
    using var file = new PEFile(assemblyPath);
    await Verify(
        new MethodToDisassemble(
            file,
            "Target",
            "OnPropertyChanged"));
}

snippet source | anchor

Result:

.method private hidebysig 
	instance void OnPropertyChanged (
		[opt] string propertyName
	) cil managed 
{
	.param [1] = nullref
		.custom instance void [System.Runtime]System.Runtime.CompilerServices.CallerMemberNameAttribute::.ctor() = (
			01 00 00 00
		)
	// Header size: 1
	// Code size: 26 (0x1a)
	.maxstack 8

	IL_0000: ldarg.0
	IL_0001: ldfld class [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler Target::PropertyChanged
	IL_0006: dup
	IL_0007: brtrue.s IL_000c

	IL_0009: pop
	IL_000a: br.s IL_0019

	IL_000c: ldarg.0
	IL_000d: ldarg.1
	IL_000e: newobj instance void [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs::.ctor(string)
	IL_0013: callvirt instance void [System.ObjectModel]System.ComponentModel.PropertyChangedEventHandler::Invoke(object, class [System.ObjectModel]System.ComponentModel.PropertyChangedEventArgs)
	IL_0018: nop

	IL_0019: ret
} // end of method Target::OnPropertyChanged

snippet source | anchor

Verify Property

[Test]
public async Task PropertyPartsUsage()
{
    using var file = new PEFile(assemblyPath);
    await Verify(
        new PropertyToDisassemble(
            file,
            "Target",
            "Property",
            PropertyParts.GetterAndSetter));
}

snippet source | anchor

Result:

.method public hidebysig specialname 
	instance string get_Property () cil managed 
{
	// Header size: 1
	// Code size: 7 (0x7)
	.maxstack 8

	IL_0000: ldarg.0
	IL_0001: ldfld string Target::'property'
	IL_0006: ret
} // end of method Target::get_Property
.method public hidebysig specialname 
	instance void set_Property (
		string 'value'
	) cil managed 
{
	// Header size: 1
	// Code size: 21 (0x15)
	.maxstack 8

	IL_0000: nop
	IL_0001: ldarg.0
	IL_0002: ldarg.1
	IL_0003: stfld string Target::'property'
	IL_0008: ldarg.0
	IL_0009: ldstr "Property"
	IL_000e: call instance void Target::OnPropertyChanged(string)
	IL_0013: nop
	IL_0014: ret
} // end of method Target::set_Property

snippet source | anchor

Settings

Starting with version 3.2 the generated IL is normalized by default, to avoid failed tests only because the binary layout has changed:

  • types and members are sorted by name
  • RVA adress comments are stripped

To turn of the sorting, use the DontNormalizeIL setting. It will then decompile IL reflecting the original layout:

[Test]
public async Task BackwardCompatibility()
{
    using var file = new PEFile(assemblyPath);
    await Verify(new TypeToDisassemble(file, "Target"))
        .DontNormalizeIl();
}

snippet source | anchor

Icon

Gem designed by Bakunetsu Kaito from The Noun Project.

About

Extends Verify to allow verification of assemblies via ICSharpCode.Decompiler

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Languages