|
// 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;
}
}
}
|