Add ComponentActivator (#23958)
authorSteve MacLean <stmaclea@microsoft.com>
Mon, 20 May 2019 20:47:45 +0000 (16:47 -0400)
committerGitHub <noreply@github.com>
Mon, 20 May 2019 20:47:45 +0000 (16:47 -0400)
Add ComponentActivator helper for loading a component
in an isolated AssemblyLoadContext and returning a delegate.

src/System.Private.CoreLib/System.Private.CoreLib.csproj
src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs [new file with mode: 0644]

index 826f365..9f7b523 100644 (file)
   <ItemGroup>
     <Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
     <Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\IsolatedComponentLoadContext.cs" />
+    <Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\ComponentActivator.cs" />
     <Compile Include="$(BclSourcesRoot)\Microsoft\Win32\UnsafeNativeMethods.cs" />
     <Compile Include="$(BclSourcesRoot)\System\__Canon.cs" />
     <Compile Include="$(BclSourcesRoot)\System\AppContext.CoreCLR.cs" />
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs
new file mode 100644 (file)
index 0000000..1ecf33f
--- /dev/null
@@ -0,0 +1,132 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+
+namespace Internal.Runtime.InteropServices
+{
+    public static class ComponentActivator
+    {
+        private static readonly Dictionary<string, IsolatedComponentLoadContext> s_AssemblyLoadContexts;
+        private static readonly Dictionary<IntPtr, Delegate> s_Delegates = new Dictionary<IntPtr, Delegate>();
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate int ComponentEntryPoint(IntPtr args, int sizeBytes);
+
+        static ComponentActivator()
+        {
+            s_AssemblyLoadContexts = new Dictionary<string, IsolatedComponentLoadContext>(StringComparer.InvariantCulture);
+        }
+
+        private static string MarshalToString(IntPtr arg, string argName)
+        {
+            if (arg == IntPtr.Zero)
+            {
+                throw new ArgumentNullException(argName);
+            }
+            string? result = Marshal.PtrToStringUTF8(arg);
+
+            if (result == null)
+            {
+                throw new ArgumentNullException(argName);
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// Native hosting entry point for creating a native delegate
+        /// </summary>
+        /// <param name="assemblyPathNative">Fully qualified path to assembly</param>
+        /// <param name="delegateTypeNative">Assembly qualified delegate type name</param>
+        /// <param name="typeNameNative">Assembly qualified type name</param>
+        /// <param name="methodNameNative">Public static method name compatible with delegateType</param>
+        /// <param name="functionHandle">Pointer where to store the function pointer result</param>
+        public static int CreateNativeDelegate(IntPtr assemblyPathNative,
+                                               IntPtr typeNameNative,
+                                               IntPtr methodNameNative,
+                                               IntPtr delegateTypeNative,
+                                               IntPtr functionHandle)
+        {
+            try
+            {
+                string assemblyPath = MarshalToString(assemblyPathNative, nameof(assemblyPathNative));
+                string typeName     = MarshalToString(typeNameNative, nameof(typeNameNative));
+                string methodName   = MarshalToString(methodNameNative, nameof(methodNameNative));
+
+                string delegateType;
+                if (delegateTypeNative == IntPtr.Zero)
+                {
+                    delegateType = typeof(ComponentEntryPoint).AssemblyQualifiedName!;
+                }
+                else
+                {
+                    delegateType = MarshalToString(delegateTypeNative, nameof(delegateTypeNative));
+                }
+
+                if (functionHandle == IntPtr.Zero)
+                {
+                    throw new ArgumentNullException(nameof(functionHandle));
+                }
+
+                Delegate d = CreateDelegate(assemblyPath, typeName, methodName, delegateType);
+
+                IntPtr functionPtr = Marshal.GetFunctionPointerForDelegate(d);
+
+                lock(s_Delegates)
+                {
+                    // Keep a reference to the delegate to prevent it from being garbage collected
+                    s_Delegates[functionPtr] = d;
+                }
+
+                Marshal.WriteIntPtr(functionHandle, functionPtr);
+            }
+            catch (Exception e)
+            {
+                return e.HResult;
+            }
+
+            return 0;
+        }
+
+        private static Delegate CreateDelegate(string assemblyPath, string typeName, string methodName, string delegateTypeName)
+        {
+            // Throws
+            IsolatedComponentLoadContext alc = GetIsolatedComponentLoadContext(assemblyPath);
+
+            Func<AssemblyName,Assembly> resolver = name => alc.LoadFromAssemblyName(name);
+
+            // Throws
+            Type type = Type.GetType(typeName, resolver, null)!;
+
+            // Throws
+            Type delegateType = Type.GetType(delegateTypeName, resolver, null)!;
+
+            // Throws
+            return Delegate.CreateDelegate(delegateType, type, methodName)!;
+        }
+
+        private static IsolatedComponentLoadContext GetIsolatedComponentLoadContext(string assemblyPath)
+        {
+            IsolatedComponentLoadContext alc;
+
+            lock (s_AssemblyLoadContexts)
+            {
+                if (!s_AssemblyLoadContexts.TryGetValue(assemblyPath, out alc))
+                {
+                    alc = new IsolatedComponentLoadContext(assemblyPath);
+                    s_AssemblyLoadContexts.Add(assemblyPath, alc);
+                }
+            }
+
+            return alc;
+        }
+    }
+}