Skip to content

Commit

Permalink
Add non-generic Create method to DispatchProxy (#73046)
Browse files Browse the repository at this point in the history
Co-authored-by: Buyaa Namnan <bunamnan@microsoft.com>
  • Loading branch information
heathbm and buyaa-n authored Oct 11, 2022
1 parent 7215b38 commit a7252b7
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public abstract partial class DispatchProxy
{
protected DispatchProxy() { }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Creating a proxy instance requires generating code at runtime")]
public static object Create([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] System.Type interfaceType, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type proxyType) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Creating a proxy instance requires generating code at runtime")]
public static T Create<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All)] T, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TProxy>() where TProxy : System.Reflection.DispatchProxy { throw null; }
protected abstract object? Invoke(System.Reflection.MethodInfo? targetMethod, object?[]? args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<root>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
Expand Down Expand Up @@ -66,10 +66,13 @@
<data name="InterfaceType_Must_Be_Interface" xml:space="preserve">
<value>The type '{0}' must be an interface, not a class.</value>
</data>
<data name="BaseType_Cannot_Be_Abstract" xml:space="preserve">
<data name="BaseType_Cannot_Be_Abstract" xml:space="preserve">
<value>The base type '{0}' cannot be abstract.</value>
</data>
<data name="PlatformNotSupported_ReflectionDispatchProxy" xml:space="preserve">
<value>This operation is not supported on .NET Standard as Reflection.Emit is not available.</value>
</data>
<data name="ProxyType_Must_Be_Derived_From_DispatchProxy" xml:space="preserve">
<value>The provided proxy type '{0}' must be derived from 'DispatchProxy'.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,32 @@ protected DispatchProxy()
public static T Create<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TProxy>()
where TProxy : DispatchProxy
{
return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T));
return (T)DispatchProxyGenerator.CreateProxyInstance(typeof(TProxy), typeof(T), "T", "TProxy");
}

/// <summary>
/// Creates an object instance that derives from class <paramref name="proxyType"/>
/// and implements interface <paramref name="interfaceType"/>.
/// </summary>
/// <param name="interfaceType">The interface the proxy should implement.</param>
/// <param name="proxyType">The base class to use for the proxy class.</param>
/// <returns>An object instance that implements <paramref name="interfaceType"/>.</returns>
/// <exception cref="System.ArgumentNullException"><paramref name="interfaceType"/> or <paramref name="proxyType"/> is null</exception>
/// <exception cref="System.ArgumentException"><paramref name="interfaceType"/> is a class,
/// or <paramref name="proxyType"/> is sealed or abstract or does not inherited from the <see cref="System.Reflection.DispatchProxy"/>
/// type or have a parameterless constructor</exception>
[RequiresDynamicCode("Creating a proxy instance requires generating code at runtime")]
public static object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type proxyType)
{
ArgumentNullException.ThrowIfNull(interfaceType);
ArgumentNullException.ThrowIfNull(proxyType);

if (!proxyType.IsAssignableTo(typeof(DispatchProxy)))
{
throw new ArgumentException(SR.Format(SR.ProxyType_Must_Be_Derived_From_DispatchProxy, proxyType.Name), nameof(proxyType));
}

return DispatchProxyGenerator.CreateProxyInstance(proxyType, interfaceType, "interfaceType", "proxyType");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ private static MethodInfo GetGenericMethodMethodInfo() =>
[RequiresDynamicCode("Defining a dynamic assembly requires generating code at runtime")]
internal static object CreateProxyInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type baseType,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType,
string interfaceParameter, string proxyParameter)
{
Debug.Assert(baseType != null);
Debug.Assert(interfaceType != null);
Expand All @@ -71,7 +72,7 @@ internal static object CreateProxyInstance(
Debug.Assert(alc != null);

ProxyAssembly proxyAssembly = s_alcProxyAssemblyMap.GetValue(alc, static x => new ProxyAssembly(x));
GeneratedTypeInfo proxiedType = proxyAssembly.GetProxyType(baseType, interfaceType);
GeneratedTypeInfo proxiedType = proxyAssembly.GetProxyType(baseType, interfaceType, interfaceParameter, proxyParameter);
return Activator.CreateInstance(proxiedType.GeneratedType, new object[] { proxiedType.MethodInfos })!;
}

Expand Down Expand Up @@ -141,7 +142,8 @@ public ProxyAssembly(AssemblyLoadContext alc)

public GeneratedTypeInfo GetProxyType(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type baseType,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType,
string interfaceParameter, string proxyParameter)
{
lock (_baseTypeAndInterfaceToGeneratedProxyType)
{
Expand All @@ -153,7 +155,7 @@ public GeneratedTypeInfo GetProxyType(

if (!interfaceToProxy.TryGetValue(interfaceType, out GeneratedTypeInfo? generatedProxy))
{
generatedProxy = GenerateProxyType(baseType, interfaceType);
generatedProxy = GenerateProxyType(baseType, interfaceType, interfaceParameter, proxyParameter);
interfaceToProxy[interfaceType] = generatedProxy;
}

Expand All @@ -166,35 +168,34 @@ public GeneratedTypeInfo GetProxyType(
Justification = "interfaceType is annotated as preserve All members, so any Types returned from GetInterfaces should be preserved as well once https://github.com/mono/linker/issues/1731 is fixed.")]
private GeneratedTypeInfo GenerateProxyType(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type baseType,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType,
string interfaceParameter, string proxyParameter)
{
// Parameter validation is deferred until the point we need to create the proxy.
// This prevents unnecessary overhead revalidating cached proxy types.

// The interface type must be an interface, not a class
if (!interfaceType.IsInterface)
{
// "T" is the generic parameter seen via the public contract
throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), "T");
throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), interfaceParameter);
}

// The base type cannot be sealed because the proxy needs to subclass it.
if (baseType.IsSealed)
{
// "TProxy" is the generic parameter seen via the public contract
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseType.FullName), "TProxy");
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseType.FullName), proxyParameter);
}

// The base type cannot be abstract
if (baseType.IsAbstract)
{
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), "TProxy");
throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), proxyParameter);
}

// The base type must have a public default ctor
if (baseType.GetConstructor(Type.EmptyTypes) == null)
{
throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), "TProxy");
throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), proxyParameter);
}

// Create a type that derives from 'baseType' provided by caller
Expand Down
Loading

0 comments on commit a7252b7

Please sign in to comment.