// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; namespace Microsoft.ReactNative.Managed { public static class ReactPackageBuilderExtensions { private static readonly Assembly s_currentAssembly = typeof(ReactPackageBuilderExtensions).Assembly; /// /// This caches for each assembly (keyed on location /// /// /// We use concurrentDictionary instead of ConcurrentBag due to it's performance characteristics. /// private static readonly ConcurrentDictionary>> s_registeredAssemblies = new ConcurrentDictionary>>(); struct AttributedModule { public Type ModuleType; public ReactModuleAttribute ModuleAttribute; public void AddToPackageBuilder(IReactPackageBuilder packageBuilder) { ReactModuleInfo moduleInfo = ReactModuleInfo.GetOrAddModuleInfo(ModuleType, ModuleAttribute); packageBuilder.AddModule(moduleInfo.ModuleName, moduleInfo.ModuleProvider); } } private static Dictionary InitAttributedModules(Assembly assembly) { var initializers = from typeInfo in assembly.DefinedTypes let moduleAttribute = typeInfo.GetCustomAttribute() where moduleAttribute != null select new AttributedModule { ModuleType = typeInfo.AsType(), ModuleAttribute = moduleAttribute }; return initializers.ToDictionary(m => ReactModuleInfo.GetModuleName(m.ModuleType, m.ModuleAttribute)); } /// /// This method registered the attributed models for the given assembly via reflection to the package builder. /// public static void AddAttributedModules(this IReactPackageBuilder packageBuilder, Assembly assembly) { var attributedModules = s_registeredAssemblies.GetOrAdd(assembly, asm => new Lazy>(() => InitAttributedModules(asm))); foreach (var attributedModule in attributedModules.Value.Values) { attributedModule.AddToPackageBuilder(packageBuilder); } } /// /// Adds the attributed models for the current assembly via reflection to the package builder. /// /// /// This method will be obsolete soon. It should only be called by existing generated project that still use the `Microsoft.ReactNative.SharedManaged` project /// where s_currentAssembly will reflect the users assembly that compiles this code as well. /// public static void AddAttributedModules(this IReactPackageBuilder packageBuilder) { AddAttributedModules(packageBuilder, s_currentAssembly); } public static bool TryAddAttributedModule(this IReactPackageBuilder packageBuilder, string moduleName) { if (s_registeredAssemblies.TryGetValue(s_currentAssembly, out var attributedModules)) { if (attributedModules.Value.TryGetValue(moduleName, out var attributedModule)) { attributedModule.AddToPackageBuilder(packageBuilder); return true; } } return false; } } }