// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; namespace Microsoft.ReactNative.Managed { public class ReactModuleInfo { private static readonly object s_moduleInfoMutex = new object(); private static Dictionary s_moduleInfos; private readonly Lazy> m_initializerInfos; private readonly Lazy> m_constantInfos; private readonly Lazy> m_constantProviderInfos; private readonly Lazy> m_methodInfos; private readonly Lazy> m_syncMethodInfos; private readonly Lazy> m_functionInfos; private readonly Lazy> m_eventInfos; public ReactModuleInfo(Type moduleType) : this(moduleType, moduleType.GetTypeInfo().GetCustomAttribute()) { } public ReactModuleInfo(Type moduleType, ReactModuleAttribute moduleAttribute) { ModuleType = moduleType; ModuleName = GetModuleName(moduleType, moduleAttribute); EventEmitterName = moduleAttribute.EventEmitterName ?? "RCTDeviceEventEmitter"; ModuleProvider = (IReactModuleBuilder moduleBuilder) => { object module = Activator.CreateInstance(moduleType); AddModuleMembers(moduleBuilder, module); return module; }; m_initializerInfos = new Lazy>(InitInitializerInfos, LazyThreadSafetyMode.PublicationOnly); m_constantInfos = new Lazy>(InitConstantInfos, LazyThreadSafetyMode.PublicationOnly); m_constantProviderInfos = new Lazy>(InitConstantProviderInfos, LazyThreadSafetyMode.PublicationOnly); m_methodInfos = new Lazy>(InitMethodInfos, LazyThreadSafetyMode.PublicationOnly); m_syncMethodInfos = new Lazy>(InitSyncMethodInfos, LazyThreadSafetyMode.PublicationOnly); m_functionInfos = new Lazy>(InitFunctionInfos, LazyThreadSafetyMode.PublicationOnly); m_eventInfos = new Lazy>(InitEventInfos, LazyThreadSafetyMode.PublicationOnly); } public static string GetModuleName(Type moduleType, ReactModuleAttribute moduleAttribute) { return moduleAttribute.ModuleName ?? moduleType.Name; } internal static ReactModuleInfo GetOrAddModuleInfo(Type moduleType, ReactModuleAttribute moduleAttribute) { lock (s_moduleInfoMutex) { if (s_moduleInfos is null) { s_moduleInfos = new Dictionary(); } if (s_moduleInfos.TryGetValue(moduleType, out ReactModuleInfo moduleInfo)) { return moduleInfo; } moduleInfo = new ReactModuleInfo(moduleType, moduleAttribute); s_moduleInfos.Add(moduleType, moduleInfo); return moduleInfo; } } private void AddModuleMembers(IReactModuleBuilder moduleBuilder, object module) { foreach (var constantInfo in m_constantInfos.Value) { constantInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var constantProviderInfo in m_constantProviderInfos.Value) { constantProviderInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var methodInfo in m_methodInfos.Value) { methodInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var syncMethodInfo in m_syncMethodInfos.Value) { syncMethodInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var eventInfo in m_eventInfos.Value) { eventInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var functionInfo in m_functionInfos.Value) { functionInfo.AddToModuleBuilder(moduleBuilder, module); } foreach (var initializerInfo in m_initializerInfos.Value) { initializerInfo.AddToModuleBuilder(moduleBuilder, module); } } private List InitInitializerInfos() { var initializers = from method in ModuleType.GetMethods(BindingFlags.Public | BindingFlags.Instance) let initializerAttribute = method.GetCustomAttribute() where initializerAttribute != null select new ReactInitializerInfo(method); return initializers.ToList(); } private List InitMethodInfos() { var result = new List(); foreach (var method in ModuleType.GetMethods()) { var methodAttribute = method.GetCustomAttribute(); if (methodAttribute != null) { var methodInfo = new ReactMethodInfo(method, methodAttribute); result.Add(methodInfo); } } return result; } private List InitSyncMethodInfos() { var result = new List(); foreach (var method in ModuleType.GetMethods()) { var syncMethodAttribute = method.GetCustomAttribute(); if (syncMethodAttribute != null) { var syncMethodInfo = new ReactSyncMethodInfo(method, syncMethodAttribute); result.Add(syncMethodInfo); } } return result; } private List InitFunctionInfos() { var fieldEvents = from field in ModuleType.GetFields(BindingFlags.Public | BindingFlags.Instance) let functionAttribute = field.GetCustomAttribute() where functionAttribute != null && !field.IsInitOnly select new ReactFunctionInfo(field, functionAttribute, ModuleName); var propertyEvents = from property in ModuleType.GetProperties(BindingFlags.Public | BindingFlags.Instance) let functionAttribute = property.GetCustomAttribute() where functionAttribute != null let propertySetter = property.SetMethod where propertySetter != null && propertySetter.IsPublic select new ReactFunctionInfo(property, functionAttribute, ModuleName); return fieldEvents.Concat(propertyEvents).ToList(); } private List InitEventInfos() { var fieldEvents = from field in ModuleType.GetFields(BindingFlags.Public | BindingFlags.Instance) let eventAttribute = field.GetCustomAttribute() where eventAttribute != null && !field.IsInitOnly select new ReactEventInfo(field, eventAttribute, EventEmitterName); var propertyEvents = from property in ModuleType.GetProperties(BindingFlags.Public | BindingFlags.Instance) let eventAttribute = property.GetCustomAttribute() where eventAttribute != null let propertySetter = property.SetMethod where propertySetter != null && propertySetter.IsPublic select new ReactEventInfo(property, eventAttribute, EventEmitterName); return fieldEvents.Concat(propertyEvents).ToList(); } private List InitConstantInfos() { var fieldConstants = from field in ModuleType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) let constantAttribute = field.GetCustomAttribute() where constantAttribute != null let constantInfo = new ReactConstantInfo(field, constantAttribute) select constantInfo; var propertyEvents = from property in ModuleType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) let constantAttribute = property.GetCustomAttribute() where constantAttribute != null let constantInfo = new ReactConstantInfo(property, constantAttribute) select constantInfo; return fieldConstants.Concat(propertyEvents).ToList(); } private List InitConstantProviderInfos() { var constantProviders = from method in ModuleType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) let constantProviderAttribute = method.GetCustomAttribute() where constantProviderAttribute != null select new ReactConstantProviderInfo(method); return constantProviders.ToList(); } public Type ModuleType { get; private set; } public string ModuleName { get; private set; } public string EventEmitterName { get; private set; } public ReactModuleProvider ModuleProvider { get; private set; } } }