Esempio n. 1
0
        /// <summary>
        /// Basic string projection where each bit is expressed, regardless of any checks.
        /// </summary>
        /// <param name="this">This CK type kind.</param>
        /// <returns>A readable string.</returns>
        public static string ToStringFlags(this CKTypeKind @this)
        {
            string[] flags = new[] { "IsAutoService",
                                     "IsScopedService",
                                     "IsSingleton",
                                     "IsRealObject",
                                     "IsPoco",
                                     "IsPocoClass",
                                     "IsFrontService",
                                     "IsFrontProcessService",
                                     "IsMarshallable",
                                     "IsMultipleService",
                                     "IsExcludedType",
                                     "HasCombinationError" };
            if (@this == CKTypeKind.None)
            {
                return("None");
            }
            var f = flags.Where((s, i) => (i == 0 && (@this & CKTypeKind.IsAutoService) != 0) ||
                                (i == 1 && (@this & CKTypeKind.IsScoped) != 0) ||
                                (i == 2 && (@this & CKTypeKind.IsSingleton) != 0) ||
                                (i == 3 && (@this & (CKTypeKind.RealObject & ~CKTypeKind.IsSingleton)) != 0) ||
                                (i == 4 && (@this & CKTypeKind.IsPoco) != 0) ||
                                (i == 5 && (@this & CKTypeKind.IsPocoClass) != 0) ||
                                (i == 6 && (@this & CKTypeKind.IsFrontService) != 0) ||
                                (i == 7 && (@this & CKTypeKind.IsFrontProcessService) != 0) ||
                                (i == 8 && (@this & CKTypeKind.IsMarshallable) != 0) ||
                                (i == 9 && (@this & CKTypeKind.IsMultipleService) != 0) ||
                                (i == 10 && (@this & CKTypeKind.IsExcludedType) != 0) ||
                                (i == 11 && (@this & CKTypeKind.HasCombinationError) != 0));

            return(String.Join("|", f));
        }
        CKTypeKind?SetLifetimeOrFrontType(IActivityMonitor m, Type t, CKTypeKind kind)
        {
            bool hasLifetime    = (kind & CKTypeKind.LifetimeMask) != 0;
            bool hasFrontType   = (kind & CKTypeKind.FrontTypeMask) != 0;
            bool isMultiple     = (kind & CKTypeKind.IsMultipleService) != 0;
            bool isMarshallable = (kind & CKTypeKind.IsMarshallable) != 0;

            Debug.Assert((kind & (IsDefiner | IsSuperDefiner)) == 0, "kind MUST not be a SuperDefiner or a Definer.");
            Debug.Assert(hasLifetime || hasFrontType || isMultiple || isMarshallable, "At least, something must be set.");
            Debug.Assert((kind & MaskPublicInfo).GetCombinationError(t.IsClass) == null, (kind & MaskPublicInfo).GetCombinationError(t.IsClass));

            // This registers the type (as long as the Type detection is concerned): there is no difference between Registering first
            // and then defining lifetime or the reverse. (This is not true for the full type registration: SetLifetimeOrFrontType must
            // not be called for an already registered type.)
            var exist = RawGet(m, t);

            if ((exist & (IsDefiner | IsSuperDefiner)) != 0)
            {
                Throw.Exception($"Type '{t}' is a Definer or a SuperDefiner. It cannot be defined as {ToStringFull( kind )}.");
            }
            var    updated = exist | kind;
            string?error   = (updated & MaskPublicInfo).GetCombinationError(t.IsClass);

            if (error != null)
            {
                m.Error($"Type '{t}' is already registered as a '{ToStringFull( exist )}'. It can not be defined as {ToStringFull( kind )}. Error: {error}");
                return(null);
            }
            _cache[t] = updated;
            Debug.Assert((updated & (IsDefiner | IsSuperDefiner)) == 0);
            Debug.Assert(CKTypeKindExtension.GetCombinationError((updated & MaskPublicInfo), t.IsClass) == null);
            return(updated & MaskPublicInfo);
        }
Esempio n. 3
0
 /// <summary>
 /// Gets the <see cref="AutoServiceKind"/> (masks the internal bits).
 /// Note that a check of the "IsFrontService => IsFrontProcessService and IsScoped" rule
 /// is made: an <see cref="ArgumentException"/> may be thrown.
 /// </summary>
 /// <param name="this">This type kind.</param>
 /// <returns>The Auto service kind.</returns>
 public static AutoServiceKind ToAutoServiceKind(this CKTypeKind @this)
 {
     if ((@this & CKTypeKind.IsFrontService) != 0
         &&
         (@this & (CKTypeKind.IsFrontProcessService | CKTypeKind.IsScoped)) != (CKTypeKind.IsFrontProcessService | CKTypeKind.IsScoped))
     {
         Throw.ArgumentException($"Invalid CKTypeKind: IsFrontService must imply IsFrontProcessService and IsScoped.");
     }
     return((AutoServiceKind)((int)@this & 63));
 }
Esempio n. 4
0
 internal MultipleImpl(Type tEnum, CKTypeKind enumerabledKind, Type tI, CKTypeKind iKind, CKTypeInfo first)
 {
     EnumerableType   = tEnum;
     _enumerabledKind = enumerabledKind;
     EnumeratedType   = tI;
     _itemKind        = iKind;
     _rawImpls        = new List <CKTypeInfo>();
     _rawImpls.Add(first);
     // These properties are null until ComputeFinalTypeKind is called.
     // (Offensive) I prefer assuming this nullity here rather than setting empty arrays.
     MarshallableTypes          = null !;
     MarshallableInProcessTypes = null !;
 }
Esempio n. 5
0
        bool DoRegisterClass(Type t, out RealObjectClassInfo?objectInfo, out AutoServiceClassInfo?serviceInfo)
        {
            Debug.Assert(t != null && t != typeof(object) && t.IsClass);

            // Skips already processed types.
            // The object collectors contains null RealObjectClassInfo and AutoServiceClassInfo value for
            // already processed types that are skipped or on error.
            serviceInfo = null;
            if (_objectCollector.TryGetValue(t, out objectInfo) ||
                _serviceCollector.TryGetValue(t, out serviceInfo))
            {
                return(false);
            }

            // Registers parent types whatever they are.
            RealObjectClassInfo? acParent = null;
            AutoServiceClassInfo?sParent  = null;

            if (t.BaseType != typeof(object))
            {
                Debug.Assert(t.BaseType != null, "Since t is not 'object'.");
                DoRegisterClass(t.BaseType, out acParent, out sParent);
            }
            CKTypeKind lt = KindDetector.GetRawKind(_monitor, t);

            if ((lt & CKTypeKind.HasCombinationError) == 0)
            {
                bool isExcluded = (lt & CKTypeKind.IsExcludedType) != 0;
                if (acParent != null || (lt & CKTypeKind.RealObject) == CKTypeKind.RealObject)
                {
                    objectInfo = RegisterObjectClassInfo(t, isExcluded, acParent);
                    Debug.Assert(objectInfo != null);
                }
                if (sParent != null || (lt & CKTypeKind.IsAutoService) != 0)
                {
                    serviceInfo = RegisterServiceClassInfo(t, isExcluded, sParent, objectInfo);
                    Debug.Assert(serviceInfo != null);
                }
            }
            // Marks the type as a registered one and gives it a chance to carry
            // Attributes...
            if (objectInfo == null && serviceInfo == null)
            {
                _objectCollector.Add(t, null);
                if ((lt & CKTypeKind.IsExcludedType) == 0)
                {
                    RegisterRegularType(t);
                }
            }
            return(true);
        }
        static string ToStringFull(CKTypeKind t)
        {
            var c = (t & MaskPublicInfo).ToStringFlags();

            if ((t & IsDefiner) != 0)
            {
                c += " [IsDefiner]";
            }
            if ((t & IsSuperDefiner) != 0)
            {
                c += " [IsSuperDefiner]";
            }
            if ((t & IsReasonMarker) != 0)
            {
                c += " [IsMarkerInterface]";
            }
            if ((t & IsLifetimeReasonExternal) != 0)
            {
                c += " [Lifetime:External]";
            }
            if ((t & IsSingletonReasonReference) != 0)
            {
                c += " [Lifetime:ReferencedBySingleton]";
            }
            if ((t & IsSingletonReasonFinal) != 0)
            {
                c += " [Lifetime:OpimizedAsSingleton]";
            }
            if ((t & IsScopedReasonReference) != 0)
            {
                c += " [Lifetime:UsesScoped]";
            }
            if ((t & IsMarshallableReasonMarshaller) != 0)
            {
                c += " [FrontType:MarshallableSinceMarshallerExists]";
            }
            if ((t & IsFrontTypeReasonExternal) != 0)
            {
                c += " [FrontType:External]";
            }
            if ((t & IsMultipleReasonExternal) != 0)
            {
                c += " [Multiple:External]";
            }
            return(c);
        }
 internal void AddMultipleMapping(Type t, CKTypeKind k, CKTypeCollector collector)
 {
     Debug.Assert(!IsSpecialized, "We are on the leaf.");
     Debug.Assert(t != Type, $"Multiple mapping {ToString()} must not be mapped to itself.");
     Debug.Assert(t.IsAssignableFrom(Type), $"Multiple mapping '{t}' must be assignable from {ToString()}!");
     Debug.Assert(_multipleMappings == null || !_multipleMappings.Contains(t), $"Multiple mapping '{t}' already registered in {ToString()}.");
     Debug.Assert(_uniqueMappings == null || !_uniqueMappings.Contains(t), $"Multiple mapping '{t}' already registered in UNIQUE mappings of {ToString()}.");
     Debug.Assert((k & CKTypeKind.IsMultipleService) != 0);
     if (_multipleMappings == null)
     {
         _multipleMappings = new List <Type>();
     }
     _multipleMappings.Add(t);
     if ((k & (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable)) != (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable))
     {
         collector.RegisterMultipleInterfaces(t, k, this);
     }
 }
        internal AutoServiceInterfaceInfo(TypeAttributesCache type, CKTypeKind lt, IEnumerable <AutoServiceInterfaceInfo> baseInterfaces)
        {
            Debug.Assert(lt == CKTypeKind.IsAutoService ||
                         lt == (CKTypeKind.IsAutoService | CKTypeKind.IsSingleton) ||
                         lt == (CKTypeKind.IsAutoService | CKTypeKind.IsScoped));
            Attributes      = type;
            InitialTypeKind = lt;
            AutoServiceInterfaceInfo[] bases = Array.Empty <AutoServiceInterfaceInfo>();
            int depth = 0;

            foreach (var iT in baseInterfaces)
            {
                depth = Math.Max(depth, iT.SpecializationDepth + 1);
                Array.Resize(ref bases, bases.Length + 1);
                bases[bases.Length - 1] = iT;
                iT.IsSpecialized        = true;
            }
            SpecializationDepth = depth;
            Interfaces          = bases;
        }
        /// <summary>
        /// Sets <see cref="AutoServiceKind"/> combination (that must not be <see cref="AutoServiceKind.None"/>) for a type.
        /// Can be called multiple times as long as no contradictory registration already exists (for instance,
        /// a <see cref="IRealObject"/> cannot be a Front service).
        /// Note that <see cref="AutoServiceKind.IsFrontService"/> is automatically expanded with <see cref="AutoServiceKind.IsScoped"/>
        /// and <see cref="AutoServiceKind.IsFrontProcessService"/>.
        /// </summary>
        /// <param name="m">The monitor.</param>
        /// <param name="t">The type to register.</param>
        /// <param name="kind">The kind of service. Must not be <see cref="AutoServiceKind.None"/>.</param>
        /// <returns>The type kind on success, null on error (errors - included combination ones - are logged).</returns>
        public CKTypeKind?SetAutoServiceKind(IActivityMonitor m, Type t, AutoServiceKind kind)
        {
            Throw.CheckArgument(kind != AutoServiceKind.None);

            bool hasFrontType = (kind & (AutoServiceKind.IsFrontProcessService | AutoServiceKind.IsFrontService)) != 0;
            bool hasLifetime  = (kind & (AutoServiceKind.IsScoped | AutoServiceKind.IsSingleton)) != 0;
            bool hasMultiple  = (kind & AutoServiceKind.IsMultipleService) != 0;

            CKTypeKind k = (CKTypeKind)kind;

            if (hasFrontType)
            {
                if ((kind & AutoServiceKind.IsFrontService) != 0)
                {
                    k          |= CKTypeKind.IsScoped;
                    hasLifetime = true;
                }
                k |= CKTypeKind.IsFrontProcessService;
            }
            string?error = k.GetCombinationError(t.IsClass);

            if (error != null)
            {
                m.Error($"Invalid Auto Service kind registration '{k.ToStringFlags()}' for type '{t}'.");
                return(null);
            }
            if (hasLifetime)
            {
                k |= IsLifetimeReasonExternal;
            }
            if (hasMultiple)
            {
                k |= IsMultipleReasonExternal;
            }
            if (hasFrontType)
            {
                k |= IsFrontTypeReasonExternal;
            }
            return(SetLifetimeOrFrontType(m, t, k));
        }
Esempio n. 10
0
 internal void RegisterMultipleInterfaces(Type tI, CKTypeKind enumeratedKind, CKTypeInfo final)
 {
     if (!_multipleMappings.TryGetValue(tI, out var multiple))
     {
         Debug.Assert(enumeratedKind.GetCombinationError(false) == null);
         // A IEnumerable of IScoped is scoped, a IEnumerable of ISingleton is singleton, a IEnumerable of IFrontProcessService is
         // itself a front process service. Even a IEnumerable of an interface that has been marked [IsMarshallable] is de facto marshallable.
         // We rely on this here.
         Type tEnumerable = typeof(IEnumerable <>).MakeGenericType(tI);
         if ((enumeratedKind & (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable)) == (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable))
         {
             // Only if the T interface is a IFrontService (and hence a Scoped) and is marked with IsMarshallable attribute
             // can we avoid the implementations analyzis. Even if a IFrontService interface marked with IsMarshallable attribute should be rare,
             // we can benefit here from this minor (but logical) optimization.
             _multipleMappings.Add(tI, new MultipleImpl(tEnumerable, tI));
         }
         else
         {
             // The IEnumerable itself may have been explicitly registered via SetAutoServiceKind.
             // We check its compatibility with its enumerated interface (there may be incoherences) later in the DoComputeFinalTypeKind.
             // Here we just check the "worst case":
             CKTypeKind enumKind = KindDetector.GetValidKind(_monitor, tEnumerable);
             Debug.Assert(enumKind.GetCombinationError(false) == null);
             if ((enumKind & (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable)) == (CKTypeKind.IsFrontService | CKTypeKind.IsMarshallable))
             {
                 _multipleMappings.Add(tI, new MultipleImpl(tEnumerable, tI));
             }
             else
             {
                 _multipleMappings.Add(tI, new MultipleImpl(tEnumerable, enumKind, tI, enumeratedKind, final));
             }
         }
     }
     else
     {
         multiple.AddImpl(final);
     }
 }
Esempio n. 11
0
        /// <summary>
        /// Gets the conflicting duplicate status message or null if this CK type kind is valid.
        /// </summary>
        /// <param name="this">This CK type kind.</param>
        /// <param name="isClass">True for Class type (not for interface).</param>
        /// <returns>An error message or null.</returns>
        public static string?GetCombinationError(this CKTypeKind @this, bool isClass)
        {
            Throw.CheckArgument(@this >= 0 && @this <= CKTypeKindDetector.MaskPublicInfo);
            // Pure predicates: checks are made against them.
            bool isAuto          = (@this & CKTypeKind.IsAutoService) != 0;
            bool isScoped        = (@this & CKTypeKind.IsScoped) != 0;
            bool isSingleton     = (@this & CKTypeKind.IsSingleton) != 0;
            bool isRealObject    = (@this & (CKTypeKind.RealObject & ~CKTypeKind.IsSingleton)) != 0;
            bool isPoco          = (@this & CKTypeKind.IsPoco) != 0;
            bool isPocoClass     = (@this & CKTypeKind.IsPocoClass) != 0;
            bool isFrontEndPoint = (@this & CKTypeKind.IsFrontService) != 0;
            bool isFrontProcess  = (@this & CKTypeKind.IsFrontProcessService) != 0;
            bool isMarshallable  = (@this & CKTypeKind.IsMarshallable) != 0;
            bool isMultiple      = (@this & CKTypeKind.IsMultipleService) != 0;

            if (isFrontEndPoint && !isFrontProcess)
            {
                Throw.ArgumentException("CKTypeKind value error: missing IsFrontProcessService flag for IsFrontService: " + @this.ToStringFlags());
            }
            if (isRealObject && !isSingleton)
            {
                Throw.Exception("CKTypeKind value error: missing IsSingleton flag to RealObject mask: " + @this.ToStringFlags());
            }

            string?conflict = null;

            if (isPoco)
            {
                if (@this != CKTypeKind.IsPoco)
                {
                    conflict = "Poco cannot be combined with any other aspect";
                }
                if (isClass)
                {
                    if (conflict != null)
                    {
                        conflict += ", ";
                    }
                    conflict += "A class cannot be a IPoco";
                }
            }
            else if (isPocoClass)
            {
                if (@this != CKTypeKind.IsPocoClass)
                {
                    conflict = "[PocoClass] class cannot be combined with any other aspect";
                }
            }
            else if (isRealObject)
            {
                Debug.Assert(isSingleton, "Checked above.");
                if (@this != CKTypeKind.RealObject)
                {
                    // If IsMultiple, then this is an interface, not a class: a IRealObject interface cannot be IsMultiple.
                    if (isScoped)
                    {
                        conflict = "RealObject cannot have a Scoped lifetime";
                    }
                    else if (isMultiple)
                    {
                        conflict = "IRealObject interface cannot be marked as a Multiple service";
                    }
                    else if (isAuto && !isClass)
                    {
                        conflict = "IRealObject interface cannot be an IAutoService";
                    }
                    // Always handle Front service.
                    if (isMarshallable)
                    {
                        if (conflict != null)
                        {
                            conflict += ", ";
                        }
                        conflict += "RealObject cannot be marked as Marshallable";
                    }
                    // Always handle Front service.
                    if (isFrontEndPoint | isFrontProcess)
                    {
                        if (conflict != null)
                        {
                            conflict += ", ";
                        }
                        conflict += "RealObject cannot be a front service";
                    }
                }
            }
            else if (isScoped && isSingleton)
            {
                if (isFrontProcess)
                {
                    conflict = "An interface or an implementation cannot be both Scoped and Singleton (since this is marked as a FrontService, this is de facto Scoped)";
                }
                else
                {
                    conflict = "An interface or an implementation cannot be both Scoped and Singleton";
                }
            }
            if (isClass)
            {
                if ((@this & CKTypeKind.IsMultipleService) != 0)
                {
                    conflict = "A class cannot be marked as a Multiple service: only interfaces can be IsMultiple.";
                }
            }
            return(conflict == null ? null : $"Invalid CK type combination: {conflict} for {(isClass ? "class" : "interface")} '{@this.ToStringFlags()}'.");
        }
Esempio n. 12
0
 /// <summary>
 /// Gets whether this <see cref="CKTypeKind"/> is <see cref="CKTypeKind.None"/> or
 /// is invalid (see <see cref="GetCombinationError(CKTypeKind,bool)"/>).
 /// </summary>
 /// <param name="this">This CK type kind.</param>
 /// <param name="isClass">True for Class type (not for interface).</param>
 /// <returns>whether this is invalid.</returns>
 public static bool IsNoneOrInvalid(this CKTypeKind @this, bool isClass)
 {
     return(@this == CKTypeKind.None || GetCombinationError(@this, isClass) != null);
 }
Esempio n. 13
0
        /// <summary>
        /// Returns a string that correctly handles flags and results to <see cref="GetCombinationError(CKTypeKind,bool)"/>
        /// if this kind is invalid.
        /// </summary>
        /// <param name="this">This CK type kind.</param>
        /// <param name="isClass">True for Class type (not for interface).</param>
        /// <returns>A readable string.</returns>
        public static string ToStringClear(this CKTypeKind @this, bool isClass)
        {
            string?error = GetCombinationError(@this, isClass);

            return(error == null?ToStringFlags(@this) : error);
        }