File: System\Runtime\InteropServices\JavaScript\JSHostImplementation.CoreCLR.cs
Web Access
Project: src\src\runtime\src\libraries\System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj (System.Runtime.InteropServices.JavaScript)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Threading;
using System.Threading.Tasks;

namespace System.Runtime.InteropServices.JavaScript
{
    internal static partial class JSHostImplementation
    {
        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "It's kept from trimming by ModuleInitializerAttribute in the generated code.")]
        [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "It's kept from trimming by ModuleInitializerAttribute in the generated code.")]
        public static Task BindAssemblyExports(string? assemblyName)
        {
            ArgumentException.ThrowIfNullOrEmpty(assemblyName);

            Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));
            Type? generatedInitializerType = assembly.GetType("System.Runtime.InteropServices.JavaScript.__GeneratedInitializer", throwOnError: true);
            if (generatedInitializerType == null)
            {
                throw new ArithmeticException($"BindAssemblyExports: __GeneratedInitializer type not found in assembly '{assemblyName}'");
            }
            MethodInfo? registerMethod = generatedInitializerType.GetMethod("__Register_", BindingFlags.NonPublic | BindingFlags.Static, binder: null, Type.EmptyTypes, modifiers: null);
            if (registerMethod == null)
            {
                throw new ArithmeticException($"BindAssemblyExports: __Register_ method not found in type '{generatedInitializerType.FullName}' in assembly '{assemblyName}'");
            }
            registerMethod.Invoke(null, null);
            return Task.CompletedTask;
        }

        // this reflection based approach is used by JSExport assemblies generated by Net10 or below
        [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "It's kept from trimming by DynamicDependencyAttribute in the generated code.")]
        [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "It's kept from trimming by DynamicDependencyAttribute in the generated code.")]
        public static unsafe JSFunctionBinding BindManagedFunction(string fullyQualifiedName, int signatureHash, ReadOnlySpan<JSMarshalerType> signatures)
        {
            var ctx = JSProxyContext.CurrentThreadContext;
            var (assemblyName, nameSpace, shortClassName, methodName) = ParseFQN(fullyQualifiedName);
            var wrapperName = $"__Wrapper_{methodName}_{signatureHash}";
            shortClassName = shortClassName.Replace('/', '+');

            // get MethodInfo from the fully qualified name
            var assembly = Assembly.Load(new AssemblyName(assemblyName));
            var clazz = string.IsNullOrEmpty(nameSpace)
                ? assembly.GetType(shortClassName)
                : assembly.GetType(nameSpace + "." + shortClassName);
            if (clazz == null)
            {
                Environment.FailFast($"Can't find {nameSpace}{shortClassName} in {assemblyName} assembly");
            }
            var wrapperInfo = clazz.GetMethod(wrapperName, BindingFlags.Static | BindingFlags.NonPublic);
            if (wrapperInfo == null)
            {
                Environment.FailFast($"Can't find method wrapper {wrapperName} in {nameSpace}.{shortClassName} in {assemblyName} assembly");
            }

            Action<IntPtr> wrapper = (IntPtr args) =>
            {
                object boxedLegacyArgs = Pointer.Box((void*)args, typeof(JSMarshalerArgument*));
                // real signature is void (JSMarshalerArgument* args)
                wrapperInfo.Invoke(null, new object?[] { boxedLegacyArgs });
            };

            int methodHandle = ctx.NextJSExportHandle++;
            ctx.JSExportByHandle[methodHandle] = wrapper;

            var signature = GetMethodSignature(signatures, null, null);

            // this will hit JS side possibly on another thread, depending on JSProxyContext.CurrentThreadContext
            JavaScriptImports.BindCSFunction(methodHandle, assemblyName, nameSpace, shortClassName, methodName, signatureHash, (IntPtr)signature.Header);

            FreeMethodSignatureBuffer(signature);

            return signature;
        }
    }
}