Пример #1
0
        /// <summary>
        /// Registers all the components in the specified assembly by looking for loadable classes
        /// and adding them to the catalog.
        /// </summary>
        /// <param name="assembly">
        /// The assembly to register.
        /// </param>
        /// <param name="throwOnError">
        /// true to throw an exception if there are errors with registering the components;
        /// false to skip any errors.
        /// </param>
        public void RegisterAssembly(Assembly assembly, bool throwOnError = true)
        {
            lock (_lock)
            {
                if (_cachedAssemblies.Add(assembly.FullName))
                {
                    foreach (LoadableClassAttributeBase attr in assembly.GetCustomAttributes(typeof(LoadableClassAttributeBase)))
                    {
                        MethodInfo      getter             = null;
                        ConstructorInfo ctor               = null;
                        MethodInfo      create             = null;
                        bool            requireEnvironment = false;
                        if (attr.InstanceType != typeof(void) && !TryGetIniters(attr.InstanceType, attr.LoaderType, attr.CtorTypes, out getter, out ctor, out create, out requireEnvironment))
                        {
                            if (throwOnError)
                            {
                                throw Contracts.Except(
                                          $"Can't instantiate loadable class '{attr.InstanceType.Name}' with name '{attr.LoadNames[0]}'");
                            }
                            Contracts.Assert(getter == null && ctor == null && create == null);
                        }
                        var info = new LoadableClassInfo(attr, getter, ctor, create, requireEnvironment);

                        AddClass(info, attr.LoadNames, throwOnError);
                    }
                }
            }
        }
Пример #2
0
        private void ScanForEntryPoints(LoadableClassInfo info)
        {
            var type = info.LoaderType;

            // Scan for entry points.
            foreach (var methodInfo in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
            {
                var attr = methodInfo.GetCustomAttributes(typeof(TlcModule.EntryPointAttribute), false).FirstOrDefault() as TlcModule.EntryPointAttribute;
                if (attr == null)
                {
                    continue;
                }

                var entryPointInfo = new EntryPointInfo(methodInfo, attr,
                                                        methodInfo.GetCustomAttributes(typeof(ObsoleteAttribute), false).FirstOrDefault() as ObsoleteAttribute);

                _entryPoints.Add(entryPointInfo);
                if (_entryPointMap.ContainsKey(entryPointInfo.Name))
                {
                    // Duplicate entry point name. We need to show a warning here.
                    // REVIEW: we will be able to do this once catalog becomes a part of env.
                    continue;
                }

                _entryPointMap[entryPointInfo.Name] = entryPointInfo;
            }

            // Scan for components.
            // First scan ourself, and then all nested types, for component info.
            ScanForComponents(type);
            foreach (var nestedType in type.GetTypeInfo().GetNestedTypes())
            {
                ScanForComponents(nestedType);
            }
        }
Пример #3
0
        private static bool TryCreateInstance <TRes>(IHostEnvironment env, Type signatureType, out TRes result, string name, string options, params object[] extra)
            where TRes : class
        {
            Contracts.CheckValue(env, nameof(env));
            env.Check(signatureType.BaseType == typeof(MulticastDelegate));
            env.CheckValueOrNull(name);

            string            nameLower = (name ?? "").ToLowerInvariant().Trim();
            LoadableClassInfo info      = FindClassCore(new LoadableClassInfo.Key(nameLower, signatureType));

            if (info == null)
            {
                result = null;
                return(false);
            }

            if (!typeof(TRes).IsAssignableFrom(info.Type))
            {
                throw env.Except("Loadable class '{0}' does not derive from '{1}'", name, typeof(TRes).FullName);
            }

            int carg = Utils.Size(extra);

            if (info.ExtraArgCount != carg)
            {
                throw env.Except(
                          "Wrong number of extra parameters for loadable class '{0}', need '{1}', given '{2}'",
                          name, info.ExtraArgCount, carg);
            }

            if (info.ArgType == null)
            {
                if (!string.IsNullOrEmpty(options))
                {
                    throw env.Except("Loadable class '{0}' doesn't support settings", name);
                }
                result = (TRes)info.CreateInstance(env, null, extra);
                return(true);
            }

            object args = info.CreateArguments();

            if (args == null)
            {
                throw Contracts.Except("Can't instantiate arguments object '{0}' for '{1}'", info.ArgType.Name, name);
            }

            ParseArguments(env, args, options, name);
            result = (TRes)info.CreateInstance(env, args, extra);
            return(true);
        }
Пример #4
0
        private void AddClass(LoadableClassInfo info, string[] loadNames, bool throwOnError)
        {
            _classes.Add(info);
            bool isEntryPoint = false;

            foreach (var sigType in info.SignatureTypes)
            {
                _signatures[sigType] = true;

                foreach (var name in loadNames)
                {
                    string nameCi = name.ToLowerInvariant();

                    var key = new LoadableClassInfo.Key(nameCi, sigType);
                    if (_classesByKey.TryGetValue(key, out var infoCur))
                    {
                        if (throwOnError)
                        {
                            throw Contracts.Except($"ComponentCatalog cannot map name '{name}' and SignatureType '{sigType}' to {info.Type.Name}, already mapped to {infoCur.Type.Name}.");
                        }
                    }
                    else
                    {
                        _classesByKey.Add(key, info);
                    }
                }

                if (sigType == typeof(SignatureEntryPointModule))
                {
                    isEntryPoint = true;
                }
            }

            if (isEntryPoint)
            {
                ScanForEntryPoints(info);
            }
        }
Пример #5
0
        private static void AddClass(LoadableClassInfo info, string[] loadNames)
        {
            _classes.Enqueue(info);
            foreach (var sigType in info.SignatureTypes)
            {
                _signatures.TryAdd(sigType, true);

                foreach (var name in loadNames)
                {
                    string nameCi = name.ToLowerInvariant();

                    var key = new LoadableClassInfo.Key(nameCi, sigType);
                    if (!_classesByKey.TryAdd(key, info))
                    {
                        var infoCur = _classesByKey[key];
                        // REVIEW: Fix this message to reflect the signature....
                        Console.Error.WriteLine(
                            "CacheClassesFromAssembly: can't map name {0} to {1}, already mapped to {2}",
                            name, info.Type.Name, infoCur.Type.Name);
                    }
                }
            }
        }
Пример #6
0
        /// <summary>
        /// This loads assemblies that are in our "root" directory (where this assembly is) and caches
        /// information for the loadable classes in loaded assemblies.
        /// </summary>
        private static void CacheLoadedAssemblies()
        {
            // The target assembly is the one containing LoadableClassAttributeBase. If an assembly doesn't reference
            // the target, then we don't want to scan its assembly attributes (there's no point in doing so).
            var target = typeof(LoadableClassAttributeBase).Assembly;

            lock (_lock)
            {
                if (_assemblyQueue == null)
                {
                    // Create the loaded assembly queue and dictionary, set up the AssemblyLoad / AssemblyResolve
                    // event handlers and populate the queue / dictionary with all assemblies that are currently loaded.
                    Contracts.Assert(_assemblyQueue == null);
                    Contracts.Assert(_loadedAssemblies == null);

                    _assemblyQueue    = new ConcurrentQueue <Assembly>();
                    _loadedAssemblies = new ConcurrentDictionary <string, Assembly>();

                    AppDomain.CurrentDomain.AssemblyLoad    += CurrentDomainAssemblyLoad;
                    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;

                    foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        // Ignore dynamic assemblies.
                        if (a.IsDynamic)
                        {
                            continue;
                        }

                        _assemblyQueue.Enqueue(a);
                        if (!_loadedAssemblies.TryAdd(a.FullName, a))
                        {
                            // Duplicate loading.
                            Console.Error.WriteLine("Duplicate loaded assembly '{0}'", a.FullName);
                        }
                    }

                    // Load all assemblies in our directory.
                    var moduleName = typeof(ComponentCatalog).Module.FullyQualifiedName;

                    // If were are loaded in the context of SQL CLR then the FullyQualifiedName and Name properties are set to
                    // string "<Unknown>" and we skip scanning current directory.
                    if (moduleName != "<Unknown>")
                    {
                        string dir = Path.GetDirectoryName(moduleName);
                        LoadAssembliesInDir(dir, true);
                        dir = Path.Combine(dir, "AutoLoad");
                        LoadAssembliesInDir(dir, true);
                    }
                }

                Contracts.AssertValue(_assemblyQueue);
                Contracts.AssertValue(_loadedAssemblies);

                Assembly assembly;
                while (_assemblyQueue.TryDequeue(out assembly))
                {
                    if (!_cachedAssemblies.Add(assembly.FullName))
                    {
                        continue;
                    }

                    if (assembly != target)
                    {
                        bool found      = false;
                        var  targetName = target.GetName();
                        foreach (var name in assembly.GetReferencedAssemblies())
                        {
                            if (name.Name == targetName.Name)
                            {
                                found = true;
                                break;
                            }
                        }
                        if (!found)
                        {
                            continue;
                        }
                    }

                    int added = 0;
                    foreach (LoadableClassAttributeBase attr in assembly.GetCustomAttributes(typeof(LoadableClassAttributeBase)))
                    {
                        MethodInfo      getter             = null;
                        ConstructorInfo ctor               = null;
                        MethodInfo      create             = null;
                        bool            requireEnvironment = false;
                        if (attr.InstanceType != typeof(void) && !TryGetIniters(attr.InstanceType, attr.LoaderType, attr.CtorTypes, out getter, out ctor, out create, out requireEnvironment))
                        {
                            Console.Error.WriteLine(
                                "CacheClassesFromAssembly: can't instantiate loadable class {0} with name {1}",
                                attr.InstanceType.Name, attr.LoadNames[0]);
                            Contracts.Assert(getter == null && ctor == null && create == null);
                        }
                        var info = new LoadableClassInfo(attr, getter, ctor, create, requireEnvironment);

                        AddClass(info, attr.LoadNames);
                        added++;
                    }
                }
            }
        }