Skip to content

Commit

Permalink
Merge pull request #357 from Washi1337/development
Browse files Browse the repository at this point in the history
5.0.0-beta.1
  • Loading branch information
Washi1337 authored Oct 18, 2022
2 parents e2d65b5 + 44ba488 commit 16362a8
Show file tree
Hide file tree
Showing 468 changed files with 17,615 additions and 2,258 deletions.
60 changes: 60 additions & 0 deletions AsmResolver.sln
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
appveyor.yml = appveyor.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.Symbols.Pdb", "src\AsmResolver.Symbols.Pdb\AsmResolver.Symbols.Pdb.csproj", "{9E311832-D0F2-42CA-84DD-9A91B88F0287}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.Symbols.Pdb.Tests", "test\AsmResolver.Symbols.Pdb.Tests\AsmResolver.Symbols.Pdb.Tests.csproj", "{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsmResolver.DotNet.Dynamic.Tests", "test\AsmResolver.DotNet.Dynamic.Tests\AsmResolver.DotNet.Dynamic.Tests.csproj", "{C089D0AB-B428-4136-89CC-7974CB590513}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsmResolver.DotNet.Dynamic", "src\AsmResolver.DotNet.Dynamic\AsmResolver.DotNet.Dynamic.csproj", "{62420213-67AD-40FC-B451-BD05C2437CE3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -424,6 +432,54 @@ Global
{2D1DF5DA-7367-4490-B3F0-B996348E150B}.Release|x64.Build.0 = Release|Any CPU
{2D1DF5DA-7367-4490-B3F0-B996348E150B}.Release|x86.ActiveCfg = Release|Any CPU
{2D1DF5DA-7367-4490-B3F0-B996348E150B}.Release|x86.Build.0 = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|x64.ActiveCfg = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|x64.Build.0 = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|x86.ActiveCfg = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Debug|x86.Build.0 = Debug|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|Any CPU.Build.0 = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|x64.ActiveCfg = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|x64.Build.0 = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|x86.ActiveCfg = Release|Any CPU
{9E311832-D0F2-42CA-84DD-9A91B88F0287}.Release|x86.Build.0 = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|x64.ActiveCfg = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|x64.Build.0 = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|x86.ActiveCfg = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Debug|x86.Build.0 = Debug|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|Any CPU.Build.0 = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x64.ActiveCfg = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x64.Build.0 = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x86.ActiveCfg = Release|Any CPU
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE}.Release|x86.Build.0 = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x64.ActiveCfg = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x64.Build.0 = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x86.ActiveCfg = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Debug|x86.Build.0 = Debug|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|Any CPU.Build.0 = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|x64.ActiveCfg = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|x64.Build.0 = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|x86.ActiveCfg = Release|Any CPU
{C089D0AB-B428-4136-89CC-7974CB590513}.Release|x86.Build.0 = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x64.ActiveCfg = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x64.Build.0 = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x86.ActiveCfg = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Debug|x86.Build.0 = Debug|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|Any CPU.Build.0 = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x64.ActiveCfg = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x64.Build.0 = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x86.ActiveCfg = Release|Any CPU
{62420213-67AD-40FC-B451-BD05C2437CE3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -461,6 +517,10 @@ Global
{40483E28-C703-4933-BA5B-9512EF6E6A21} = {EA971BB0-94BA-44DB-B16C-212D2DB27E17}
{CF6A7E02-37DC-4963-AC14-76D74ADCD87A} = {B3AF102B-ABE1-41B2-AE48-C40702F45AB0}
{2D1DF5DA-7367-4490-B3F0-B996348E150B} = {B3AF102B-ABE1-41B2-AE48-C40702F45AB0}
{9E311832-D0F2-42CA-84DD-9A91B88F0287} = {34A95168-A162-4F6A-803B-B6F221FE9EA6}
{AAD604B6-ABE5-4DBC-A2D9-4EF8E815B2EE} = {786C1732-8C96-45DD-97BB-639C9AA7F45B}
{C089D0AB-B428-4136-89CC-7974CB590513} = {786C1732-8C96-45DD-97BB-639C9AA7F45B}
{62420213-67AD-40FC-B451-BD05C2437CE3} = {34A95168-A162-4F6A-803B-B6F221FE9EA6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3302AC79-6D23-4E7D-8C5F-C0C7261044D0}
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<RepositoryUrl>https://github.com/Washi1337/AsmResolver</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<LangVersion>10</LangVersion>
<Version>4.11.2</Version>
<Version>5.0.0-beta.1</Version>
</PropertyGroup>

</Project>
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- master

image: Visual Studio 2022
version: 4.11.2-master-build.{build}
version: 5.0.0-master-build.{build}
configuration: Release

skip_commits:
Expand Down Expand Up @@ -33,7 +33,7 @@
- development

image: Visual Studio 2022
version: 4.11.2-dev-build.{build}
version: 5.0.0-dev-build.{build}
configuration: Release

skip_commits:
Expand Down
159 changes: 139 additions & 20 deletions docs/dotnet/cloning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,30 @@ To help developers in injecting existing code into a module, ``AsmResolver.DotNe
The MemberCloner class
----------------------

The ``MemberCloner`` is the root object responsible for cloning members in a .NET module, and importing them into another.
The ``MemberCloner`` is the root object responsible for cloning members in a .NET module, and importing them into another.

In the snippet below, we define a new ``MemberCloner`` that is able to clone and import members into the module ``destinationModule:``.

.. code-block:: csharp
ModuleDefinition destinationModule = ...
MemberCloner cloner = new MemberCloner(destinationModule);
var cloner = new MemberCloner(destinationModule);
In the remaining sections of this article, we assume that the ``MemberCloner`` is initialized using the code above.


Include members to clone
------------------------
Include members
---------------

The general idea of the ``MemberCloner`` is to first provide all the members to be cloned, and then clone everything all in one go. The reason why it is done like this, is to allow the ``MemberCloner`` to fix up any cross references to members within the to-be-cloned metadata and CIL code.
The general idea of the ``MemberCloner`` is to first provide all the members to be cloned, and then clone everything all in one go. This is to allow the ``MemberCloner`` to fix up any cross references to members within the to-be-cloned metadata and CIL code.

For the sake of the example, we assume that the following two classes are to be injected in ``destinationModule``:

.. code-block:: csharp
public class Rectangle
{
public Rectangle(Vector2 location, Vector2 size)
public Rectangle(Vector2 location, Vector2 size)
{
Location = location;
Size = size;
Expand All @@ -50,7 +50,7 @@ For the sake of the example, we assume that the following two classes are to be
public class Vector2
{
public Vector2(int x, int y)
public Vector2(int x, int y)
{
X = x;
Y = y;
Expand All @@ -60,14 +60,15 @@ For the sake of the example, we assume that the following two classes are to be
public int Y { get; set; }
}
The first thing we then should do, is find the type definitions that correspond to these classes:
The first step in cloning involves loading the source module, and finding the type definitions that correspond to these classes:

.. code-block:: csharp
var sourceModule = ModuleDefinition.FromFile(...);
var rectangleType = sourceModule.TopLevelTypes.First(t => t.Name == "Rectangle");
var vectorType = sourceModule.TopLevelTypes.First(t => t.Name == "Vector2");
Alternatively, if the source assembly is loaded by the CLR, we also can look up the members by metadata token.

.. code-block:: csharp
Expand All @@ -84,41 +85,147 @@ We can then use ``MemberCloner.Include`` to include the types in the cloning pro
cloner.Include(rectangleType, recursive: true);
cloner.Include(vectorType, recursive: true);
The ``recursive`` parameter indicates whether all members and nested types need to be included as well.
The ``recursive`` parameter indicates whether all members and nested types need to be included as well. This value is ``true`` by default and can also be omitted.

.. code-block:: csharp
cloner.Include(rectangleType);
cloner.Include(vectorType);
``Include`` returns the same ``MemberCloner`` instance. It is therefore also possible to create a long method chain of members to include in the cloning process.

.. code-block:: csharp
cloner
.Include(rectangleType, recursive: true)
.Include(vectorType, recursive: true);
.Include(rectangleType)
.Include(vectorType);
Cloning individual methods, fields, properties and/or events is also supported. This can be done by including the corresponding ``MethodDefinition``, ``FieldDefinition``, ``PropertyDefinition`` and/or ``EventDefinition`` instead.


Cloning the included members
Cloning the included members
----------------------------

When all members are included, it is possible to call ``MemberCloner.Clone`` to clone them all in one go.
When all members are included, it is possible to call ``MemberCloner.Clone`` to clone them all in one go.

.. code-block:: csharp
var result = cloner.Clone();
The ``MemberCloner`` will automatically resolve any cross references between types, fields and methods that are included in the cloning process.
The ``MemberCloner`` will automatically resolve any cross references between types, fields and methods that are included in the cloning process.

For instance, going with the example in the previous section, if both the ``Rectangle`` as well as the ``Vector2`` classes are included, any reference in ``Rectangle`` to ``Vector2`` will be replaced with a reference to the cloned ``Vector2``. If not all members are included, the ``MemberCloner`` will assume that these are references to external libraries, and will use the ``ReferenceImporter`` to construct references to these members instead.

.. warning::

The ``MemberCloner`` heavily depends on the ``ReferenceImporter`` class for copying references into the destination module. This class has some limitations, in particular on importing / cloning from modules targeting different framework versions. See :ref:`dotnet-importer-common-caveats` for more information.
Custom reference importers
--------------------------

The ``MemberCloner`` heavily depends on the ``CloneContextAwareReferenceImporter`` class for copying references into the destination module. This class is derived from ``ReferenceImporter``, which has some limitations. In particular, limitations arise when cloning from modules targeting different framework versions, or when trying to reference members that may already exist in the target module (e.g., when dealing with ``NullableAttribute`` annotated metadata).

To account for these situations, the cloner allows for specifying custom reference importer instances. By deriving from the ``CloneContextAwareReferenceImporter`` class and overriding methods such as ``ImportMethod``, we can reroute specific member references to the appropriate metadata if needed. Below is an example of a basic implementation of an importer that attempts to map method references from the ``System.Runtime.CompilerServices`` namespace to definitions that are already present in the target module:

Injecting the cloned members
.. code-block:: csharp
public class MyImporter : CloneContextAwareReferenceImporter
{
private static readonly SignatureComparer Comparer = new();
public MyImporter(MemberCloneContext context)
: base(context)
{
}
public override IMethodDefOrRef ImportMethod(IMethodDefOrRef method)
{
// Check if the method is from a type defined in the System.Runtime.CompilerServices namespace.
if (method.DeclaringType is { Namespace.Value: "System.Runtime.CompilerServices" } type)
{
// We might already have a type and method defined in the target module (e.g., NullableAttribute::.ctor(int32)).
// Try find it in the target module.
var existingMethod = this.Context.Module
.TopLevelTypes.FirstOrDefault(t => t.IsTypeOf(type.Namespace, type.Name))?
.Methods.FirstOrDefault(m => method.Name == m.Name && Comparer.Equals(m.Signature, method.Signature));
// If we found a matching definition, then return it instead of importing the reference.
if (existingMethod is not null)
return existingMethod;
}
return base.ImportMethod(method);
}
}
We can then pass a custom importer factory to our member cloner constructor as follows:

.. code-block:: csharp
var cloner = new MemberCloner(destinationModule, context => new MyImporter(context));
All references to methods defined in the ``System.Runtime.CompilerServices`` namespace will then be mapped to the appropriate method definitions if they exist in the target module.

See :ref:`dotnet-importer-common-caveats` for more information on reference importing and its caveats.


Post processing of cloned members
---------------------------------

In some cases, cloned members may need to be post-processed before they are injected into the target module. The ``MemberCloner`` class can be initialized with an instance of a ``IMemberClonerListener``, that gets notified by the cloner object every time a definition was cloned.

Below an example that appends the string ``_Cloned`` to the name for every cloned type.

.. code-block:: csharp
public class MyListener : MemberClonerListener
{
public override void OnClonedType(TypeDefinition original, TypeDefinition cloned)
{
cloned.Name = $"{original.Name}_Cloned";
base.OnClonedType(original, cloned);
}
}
We can then initialize our cloner with an instance of our listener class:

.. code-block:: csharp
var cloner = new MemberCloner(destinationModule, new MyListener());
Alternatively, we can also override the more generic ``OnClonedMember`` instead, which gets fired for every member definition that was cloned.

.. code-block:: csharp
public class MyListener : MemberClonerListener
{
public override void OnClonedMember(IMemberDefinition original, IMemberDefinition cloned)
{
/* ... Do post processing here ... */
base.OnClonedMember(original, cloned);
}
}
As a shortcut, this can also be done by passing in a delegate or lambda instead to the ``MemberCloner`` constructor.

.. code-block:: csharp
var cloner = new MemberCloner(destinationModule, (original, cloned) => {
/* ... Do post processing here ... */
});
Injecting the cloned members
----------------------------

After cloning, we obtain a ``MemberCloneResult``, which contains a register of all members cloned by the member cloner.
The ``Clone`` method returns a ``MemberCloneResult``, which contains a register of all members cloned by the member cloner.

- ``OriginalMembers``: The collection containing all original members.
- ``ClonedMembers``: The collection containing all cloned members.
Expand All @@ -136,9 +243,21 @@ Alternatively, we can get all cloned top-level types.
var clonedTypes = result.ClonedTopLevelTypes;
It is important to note that the ``MemberCloner`` class itself does not inject any of the cloned members. To inject the cloned types, we can for instance add them to the ``ModuleDefinition.TopLevelTypes`` collection:
It is important to note that the ``MemberCloner`` class itself does not inject any of the cloned members by itself. To inject the cloned types, we can for instance add them to the ``ModuleDefinition.TopLevelTypes`` collection:

.. code-block:: csharp
foreach (var clonedType in clonedTypes)
destinationModule.TopLevelTypes.Add(clonedType);
destinationModule.TopLevelTypes.Add(clonedType);
However, since injecting the cloned top level types is a very common use-case for the cloner, AsmResolver defines the ``InjectTypeClonerListener`` class that implements a cloner listener that injects all top level types automatically into the destination module. In such a case, the code can be reduced to the following:

.. code-block:: csharp
new MemberCloner(destinationModule, new InjectTypeClonerListener(destinationModule))
.Include(rectangleType)
.Include(vectorType)
.Clone();
// `destinationModule` now contains copies of `rectangleType` and `vectorType`.
Loading

0 comments on commit 16362a8

Please sign in to comment.