Skip to content

Commit

Permalink
Add TypeRef.FromDllFile overload with Type
Browse files Browse the repository at this point in the history
  • Loading branch information
ltrzesniewski committed Mar 23, 2024
1 parent e1eb1a5 commit 1838f56
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 49 deletions.
11 changes: 11 additions & 0 deletions src/InlineIL.Fody/Model/TypeRefBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ public static TypeRefBuilder FromInjectedAssembly(ModuleWeavingContext context,
: throw new WeavingException($"Could not find type '{typeName}' in assembly: {assemblyPath}");
}

public static TypeRefBuilder FromInjectedAssembly(ModuleWeavingContext context, string assemblyPath, TypeReference typeRef)
{
return typeRef switch
{
GenericParameter => throw new WeavingException("Generic parameters cannot be used in this context, expecting a type which can be defined in an assembly."),
FunctionPointerType => throw new WeavingException("Function pointer types cannot be used in this context, expecting a type which can be defined in an assembly."),
TypeSpecification => throw new WeavingException($"The provided type does not represent an element type: '{typeRef.FullName}', did you mean '{typeRef.GetElementType().FullName}'?"),
_ => FromInjectedAssembly(context, assemblyPath, typeRef.FullName)
};
}

private static TypeReference FindType(ModuleDefinition module, string assemblyName, string typeName)
{
var assembly = assemblyName == module.Assembly.Name.Name
Expand Down
11 changes: 11 additions & 0 deletions src/InlineIL.Fody/Processing/ArgumentConsumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ private TypeRefBuilder ConsumeArgTypeRefBuilder(Instruction instruction)
return builder;
}

case "InlineIL.TypeRef InlineIL.TypeRef::FromDllFile(System.String,System.Type)":
{
var args = _il.GetArgumentPushInstructionsInSameBasicBlock(instruction);
var assemblyPath = ConsumeArgString(args[0]);
var typeRef = ConsumeArgTypeRef(args[1]);
var builder = TypeRefBuilder.FromInjectedAssembly(_context, assemblyPath, typeRef);

_il.Remove(instruction);
return builder;
}

default:
throw UnexpectedInstruction(instruction, "a type reference");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using InlineIL.Tests.InjectedAssembly;
using static InlineIL.IL.Emit;

namespace InlineIL.Tests.InvalidAssemblyToProcess;

#pragma warning disable CS0618

public partial class TypeRefTestCases
{
private const string _injectedAltAssemblyPath = "InjectedDllDir/InlineIL.Tests.InjectedAssembly.Alternative.dll";

public void UseMethodsFromDifferentVersionsOfDll()
{
// Use referenced DLL
InjectedType.AddInt32(40, 2);

// Use alternative version of the referenced DLL
Ldc_I4(40);
Ldc_I4_2();

Call(
MethodRef.Method(
TypeRef.FromDllFile(_injectedAltAssemblyPath, "InlineIL.Tests.InjectedAssembly.InjectedType"),
"MultiplyInt32"
)
);

Pop();
}

public void UseMethodsFromDifferentVersionsOfDllUsingTypeReference()
{
// Use referenced DLL
InjectedType.AddInt32(40, 2);

// Use alternative version of the referenced DLL
Ldc_I4(40);
Ldc_I4_2();

Call(
MethodRef.Method(
TypeRef.FromDllFile(_injectedAltAssemblyPath, typeof(InjectedType)),
"MultiplyInt32"
)
);

Pop();
}

public void InvalidInjectedDllFile()
{
Ldtoken(
TypeRef.FromDllFile("InjectedDllDir/DoesNotExist.dll", "SomeType")
);
}

public void InvalidInjectedTypeName()
{
Ldtoken(
TypeRef.FromDllFile(_injectedAltAssemblyPath, "DoesNotExist")
);
}

public void InvalidInjectedTypeSpec()
{
Ldtoken(
TypeRef.FromDllFile(_injectedAltAssemblyPath, typeof(InjectedType[]))
);
}

public void InvalidInjectedTypeSpec2()
{
Ldtoken(
TypeRef.FromDllFile(_injectedAltAssemblyPath, typeof(InjectedType).MakeByRefType())
);
}

public void InvalidInjectedFnPtr()
{
Ldtoken(
TypeRef.FromDllFile(_injectedAltAssemblyPath, typeof(delegate*<int, void>))
);
}

public void InvalidInjectedGenericParam<T>()
{
Ldtoken(
TypeRef.FromDllFile(_injectedAltAssemblyPath, typeof(T))
);
}
}
45 changes: 1 addition & 44 deletions src/InlineIL.Tests.InvalidAssemblyToProcess/TypeRefTestCases.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using InlineIL.Tests.InjectedAssembly;
using static InlineIL.IL.Emit;

namespace InlineIL.Tests.InvalidAssemblyToProcess;

[SuppressMessage("ReSharper", "UnusedType.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
public class TypeRefTestCases
public partial class TypeRefTestCases
{
public void LoadNullType()
{
Expand Down Expand Up @@ -109,46 +108,4 @@ public void InvalidGenericParameterIndex()
{
Ldtoken(TypeRef.TypeGenericParameters[-1]);
}

public void UseMethodsFromDifferentVersionsOfDll()
{
// Use referenced DLL
InjectedType.AddInt32(40, 2);

// Use alternative version of the referenced DLL
Ldc_I4(40);
Ldc_I4_2();

Call(
MethodRef.Method(
#pragma warning disable CS0618
TypeRef.FromDllFile(
"InjectedDllDir/InlineIL.Tests.InjectedAssembly.Alternative.dll",
"InlineIL.Tests.InjectedAssembly.InjectedType"
),
#pragma warning restore CS0618
"MultiplyInt32"
)
);

Pop();
}

public void InvalidInjectedDllFile()
{
Ldtoken(
#pragma warning disable CS0618
TypeRef.FromDllFile("InjectedDllDir/DoesNotExist.dll", "SomeType")
#pragma warning restore CS0618
);
}

public void InvalidInjectedTypeName()
{
Ldtoken(
#pragma warning disable CS0618
TypeRef.FromDllFile("InjectedDllDir/InlineIL.Tests.InjectedAssembly.Alternative.dll", "DoesNotExist")
#pragma warning restore CS0618
);
}
}
36 changes: 31 additions & 5 deletions src/InlineIL.Tests/Weaving/TypeRefTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,13 +347,15 @@ public void should_return_constructed_generic_type_spec_from_injected_type()
Type.GetTypeFromHandle(result).ShouldEqual(typeof(InjectedGenericType<InjectedType>));
}

[Fact]
public void should_use_methods_from_injected_type_and_referenced_type()
[Theory]
[InlineData(nameof(TypeRefTestCases.UseMethodsFromDifferentVersionsOfDll))]
[InlineData(nameof(TypeRefTestCases.UseMethodsFromDifferentVersionsOfDllUsingTypeReference))]
public void should_use_methods_from_injected_type_and_referenced_type(string methodName)
{
var calls = InvalidAssemblyToProcessFixture.ResultModule
.GetType(typeof(TypeRefTestCases).FullName)
.Methods
.Single(i => i.Name == nameof(TypeRefTestCases.UseMethodsFromDifferentVersionsOfDll))
.Single(i => i.Name == methodName)
.Body
.Instructions
.Where(i => i.OpCode.Code == Code.Call)
Expand All @@ -368,13 +370,37 @@ public void should_use_methods_from_injected_type_and_referenced_type()
[Fact]
public void should_report_dll_file_not_found()
{
ShouldHaveError("InvalidInjectedDllFile").ShouldContain("Could not read assembly");
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedDllFile)).ShouldContain("Could not read assembly");
}

[Fact]
public void should_report_type_in_dll_file_not_found()
{
ShouldHaveError("InvalidInjectedTypeName").ShouldContain("Could not find type 'DoesNotExist'");
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedTypeName)).ShouldContain("Could not find type 'DoesNotExist'");
}

[Fact]
public void should_report_injected_type_spec()
{
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedTypeSpec)).ShouldContain("The provided type does not represent an element type");
}

[Fact]
public void should_report_injected_type_spec_2()
{
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedTypeSpec2)).ShouldContain("The provided type does not represent an element type");
}

[Fact]
public void should_report_injected_fn_ptr()
{
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedFnPtr)).ShouldContain("Function pointer types cannot be used in this context");
}

[Fact]
public void should_report_injected_generic_param()
{
ShouldHaveError(nameof(TypeRefTestCases.InvalidInjectedGenericParam)).ShouldContain("Generic parameters cannot be used in this context");
}
}

Expand Down
28 changes: 28 additions & 0 deletions src/InlineIL/TypeRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,32 @@ public TypeRef WithRequiredModifier(TypeRef modifierType)
#endif
public static TypeRef FromDllFile(string assemblyPath, string typeName)
=> throw IL.Throw();

/// <summary>
/// <b>EXPERIMENTAL API</b> - Returns a reference to <paramref name="type"/> as defined in a different assembly specified by its file path,
/// absolute or relative to the project directory.
/// A reference to that assembly name will be added if it is different from the one the provided <paramref name="type"/> is defined in.
/// </summary>
/// <param name="assemblyPath">The path to an assembly file, either absolute or relative to the project directory.</param>
/// <param name="type">The element type whose definition should be present in the provided assembly file.</param>
/// <returns>A <see cref="TypeRef"/> to the given type.</returns>
/// <remarks>
/// <para>
/// This API is marked as experimental as it is meant for specific <i>testing</i> purposes only. It can silently add a reference to an assembly
/// which will not necessarily be resolvable at runtime.
/// </para>
/// <para>
/// Some features are not supported, such as forwarded types.
/// Behavior may change between minor or patch releases.
/// </para>
/// </remarks>
#if NET8_0_OR_GREATER
[Experimental("InlineIL0100")]
#elif NET5_0_OR_GREATER
[Obsolete("This is an experimental API. Use it at your own risk inside a #pragma warning disable InlineIL0100 block.", DiagnosticId = "InlineIL0100")]
#else
[Obsolete("This is an experimental API. Use it at your own risk inside a #pragma warning disable CS0618 block.")]
#endif
public static TypeRef FromDllFile(string assemblyPath, Type type)
=> throw IL.Throw();
}

0 comments on commit 1838f56

Please sign in to comment.