void CheckStObjProperties(IActivityMonitor monitor, BuildValueCollector valueCollector) { if (_stObjProperties == null) { return; } foreach (StObjProperty p in _stObjProperties) { if (p.InfoOnType == null || p.InfoOnType.ResolutionSource == PropertyResolutionSource.FromContainerAndThenGeneralization) { // Check the Type constraint that could potentially hurt one day. bool containerHasSetOrMerged = IsOwnContainer && HandleStObjPropertySource(monitor, p, _dContainer !, "Container", true); if (Generalization != null) { HandleStObjPropertySource(monitor, p, Generalization, "Generalization", !containerHasSetOrMerged); } } else if (p.InfoOnType.ResolutionSource == PropertyResolutionSource.FromGeneralizationAndThenContainer) { // Check the Type constraint that could potentially hurt one day. bool generalizationHasSetOrMerged = Generalization != null && HandleStObjPropertySource(monitor, p, Generalization, "Generalization", true); if (IsOwnContainer) { HandleStObjPropertySource(monitor, p, _dContainer !, "Container", !generalizationHasSetOrMerged); } } // If the value is missing (it has never been set or has been explicitly "removed"), we have nothing to do. // If the type is not constrained, we have nothing to do. object v = p.Value; if (v != System.Type.Missing) { bool setIt = p.HasStructuredObjectProperty; if (p.Type != typeof(object)) { if (v == null) { if (p.Type.IsValueType && !(p.Type.IsGenericType && p.Type.GetGenericTypeDefinition() == typeof(Nullable <>))) { monitor.Error($"StObjProperty '{ToString()}.{p.Name}' has been set to null but its type '{p.Type.Name}' is not nullable."); setIt = false; } } else { if (!p.Type.IsAssignableFrom(v.GetType())) { monitor.Error($"StObjProperty '{ToString()}.{p.Name}' is of type '{p.Type.Name}', but a value of type '{v.GetType()}' has been set."); setIt = false; } } } if (setIt) { Debug.Assert(p.InfoOnType != null); AddPreConstructProperty(p.InfoOnType.PropertyInfo, v, valueCollector); } } } }
void AddPreConstructProperty(PropertyInfo p, object?o, BuildValueCollector valueCollector) { if (_preConstruct == null) { _preConstruct = new List <PropertySetter>(); } _preConstruct.Add(new PropertySetter(p, o, valueCollector)); }
void AddPostBuildProperty(PropertyInfo p, object o, BuildValueCollector valueCollector) { Debug.Assert(Specialization == null, "Called on leaf only."); if (_leafData.PostBuildProperties == null) { _leafData.PostBuildProperties = new List <PropertySetter>(); } _leafData.PostBuildProperties.Add(new PropertySetter(p, o, valueCollector)); }
private void DoCallStObjConstruct(IActivityMonitor monitor, BuildValueCollector valueCollector, IStObjValueResolver?valueResolver, MethodInfo stobjConstruct, IReadOnlyList <MutableParameter> mutableParameters) { object?[] parameters = new object[mutableParameters.Count]; int i = 0; foreach (MutableParameter t in mutableParameters) { // We inject our "setup monitor" for IActivityMonitor parameter type. if (t.IsSetupLogger) { t.SetParameterValue(monitor); t.BuilderValueIndex = Int32.MaxValue; } else { MutableItem?resolved = null; if (t.Value == System.Type.Missing) { // Parameter reference have already been resolved as dependencies for graph construction since // no Value has been explicitly set for the parameter. resolved = t.CachedResolvedStObj; if (resolved != null) { Debug.Assert(resolved.InitialObject != System.Type.Missing); t.SetParameterValue(resolved.InitialObject); } } if (valueResolver != null) { valueResolver.ResolveParameterValue(monitor, t); } if (t.Value == System.Type.Missing && !t.IsRealParameterOptional) { if (!t.IsOptional) { // By throwing an exception here, we stop the process and avoid the construction // of an invalid object graph... // This behavior (FailFastOnFailureToResolve) may be an option once. For the moment: log the error. monitor.Fatal($"{t}: Unable to resolve non optional. Attempting to use a default value to continue the setup process in order to detect other errors."); } Debug.Assert(t.Type != null); t.SetParameterValue(t.Type.IsValueType ? Activator.CreateInstance(t.Type) : null); } if (resolved != null && t.Value == resolved.InitialObject) { t.BuilderValueIndex = -(resolved.IndexOrdered + 1); } else { t.BuilderValueIndex = valueCollector.RegisterValue(t.Value); } } parameters[i++] = t.Value; } stobjConstruct.Invoke(_leafData.StructuredObject, parameters); }
internal PropertySetter(PropertyInfo p, object?o, BuildValueCollector valueCollector) { Property = p; Value = o; if (o is MutableItem) { IndexValue = -1; } else { IndexValue = valueCollector.RegisterValue(o); } }
internal void RegisterRemainingDirectPropertiesAsPostBuildProperties(BuildValueCollector valueCollector) { if (Specialization == null && _leafData.DirectPropertiesToSet != null) { foreach (var k in _leafData.DirectPropertiesToSet) { if (k.Value != System.Type.Missing) { AddPostBuildProperty(k.Key, k.Value, valueCollector); } } _leafData.DirectPropertiesToSet.Clear(); } }
internal void CallConstruct(IActivityMonitor monitor, BuildValueCollector valueCollector, IStObjValueResolver?valueResolver) { Debug.Assert(_constructParameterEx != null, "Always allocated."); if (_preConstruct != null) { foreach (var p in _preConstruct) { SetPropertyValue(monitor, p); } } if (_constructParametersAbove != null) { foreach (var above in _constructParametersAbove) { DoCallStObjConstruct(monitor, valueCollector, valueResolver, above.Item1, above.Item2); } } if (RealObjectType.StObjConstruct != null) { Debug.Assert(_constructParameterEx != null); DoCallStObjConstruct(monitor, valueCollector, valueResolver, RealObjectType.StObjConstruct, _constructParameterEx); } }
/// <summary> /// Works on leaf only. /// Registers all the DirectProperties values by calling RootGeneralization.AddPreConstructProperty: they will be set right before the call to StObjConstruct of the root of the inheritance chain. /// Registers all the InjectObjects (resolves the MutableItem) by calling AddPostBuildProperty on the most specialized leaf: these properties will be set after the whole graph /// will be created. /// For AmbientProperties, it is slightly more complicated: depending of the property, we will be able to set it before StObjConstruct (like DirectProperties) or only after the whole graph /// is created. /// - When the AmbientProperty is a mere value (not a StObj), we can call RootGeneralization.AddPreConstructProperty. /// - When the AmbientProperty is a StObj, depending on the resolved StObj's TrackAmbientPropertyMode, we call RootGeneralization.AddPreConstructProperty only if /// we can be sure that the target StObj (not necessarily its specialization) will be constructed before this object. /// This is where the TrackedAmbientPropertyInfo is added to the target and where covariance is handled. /// (This is also where, each time I look at this code, I ask myself "wtf...???" :-).) /// </summary> internal void ResolvePreConstructAndPostBuildProperties( IActivityMonitor monitor, BuildValueCollector valueCollector, IStObjValueResolver?valueResolver) { Debug.Assert(Specialization == null && _leafData.LeafSpecialization == this, "We are on the ultimate (leaf) Specialization."); // Here we AddPreConstructProperty the current direct properties: they will be set on the final object before // the call to StObjConstruct. // We flush the dictionary and the next calls to SetDirectProperty will be AddPostBuildProperty. // This enforces the coherency between build time and runtime. if (_leafData.DirectPropertiesToSet != null) { foreach (var k in _leafData.DirectPropertiesToSet) { if (k.Value != System.Type.Missing) { RootGeneralization.AddPreConstructProperty(k.Key, k.Value, valueCollector); } } _leafData.DirectPropertiesToSet.Clear(); } foreach (var c in _leafData.AllInjectObjects) { MutableItem?m = c.ResolveToStObj(monitor, EngineMap); if (m != null) { AddPostBuildProperty(c.InjecttInfo.SettablePropertyInfo, m, valueCollector); } } // Use _ambientPropertiesEx to work on a fixed set of MutableAmbientProperty that // correspond to the ones of this object (without the cached ones that may appear at the end of the list). foreach (var a in _ambientPropertiesEx) { Debug.Assert(a.Type != null); EnsureCachedAmbientProperty(monitor, a.Type, a.Name, a); if (a.Value == System.Type.Missing) { if (valueResolver != null) { valueResolver.ResolveExternalPropertyValue(monitor, a); } } object?value = a.Value; if (value == System.Type.Missing) { if (!a.IsOptional) { monitor.Error($"{a.ToString()}: Unable to resolve non optional."); } } else { // Ambient property setting: when it is a StObj, it depends on the relationship between the items. MutableItem?resolved = value as MutableItem; // If the property value is a StObj, extracts its actual value. if (resolved != null) { #region AmbientProperty is a StObj. MutableItem?highestSetSource = null; MutableItem?highestSetResolved = null; MutableItem? source = this; AmbientPropertyInfo?sourceProp = a.AmbientPropertyInfo; Debug.Assert(sourceProp.Index < source.RealObjectType.AmbientProperties.Count, "This is the way to test if the property is defined at the source level or not."); // Walks up the chain to locate the most abstract compatible slice. { MutableItem?genResolved = resolved.Generalization; while (genResolved != null && sourceProp.PropertyType.IsAssignableFrom(genResolved.ClassType)) { resolved = genResolved; genResolved = genResolved.Generalization; } } if (resolved._trackedAmbientProperties != null) { if (resolved._trackAmbientPropertiesMode == TrackAmbientPropertiesMode.AddPropertyHolderAsChildren || resolved._trackAmbientPropertiesMode == TrackAmbientPropertiesMode.PropertyHolderRequiresThis) { highestSetSource = source; highestSetResolved = resolved; } resolved._trackedAmbientProperties.Add(new TrackedAmbientPropertyInfo(source, sourceProp)); } // Walks up the source chain and adjusts the resolved target accordingly. while ((source = source.Generalization) != null && resolved._needsTrackedAmbientProperties) { bool sourcePropChanged = false; Debug.Assert((source == null) == (sourceProp == null)); // If source does not define anymore sourceProp. Does it define the property with another type? while (source != null && sourceProp !.Index >= source.RealObjectType.AmbientProperties.Count) { sourcePropChanged = true; if ((sourceProp = sourceProp.Generalization) == null) { // No property defined anymore at this level: we do not have anything more to do. source = null; } } if (source == null) { break; } Debug.Assert(sourceProp != null); // Walks up the chain to locate the most abstract compatible slice. if (sourcePropChanged) { MutableItem?genResolved = resolved.Generalization; while (genResolved != null && sourceProp.PropertyType.IsAssignableFrom(genResolved.ClassType)) { resolved = genResolved; genResolved = genResolved.Generalization; } } if (resolved._trackedAmbientProperties != null) { if (resolved._trackAmbientPropertiesMode == TrackAmbientPropertiesMode.AddPropertyHolderAsChildren || resolved._trackAmbientPropertiesMode == TrackAmbientPropertiesMode.PropertyHolderRequiresThis) { highestSetSource = source; highestSetResolved = resolved; } resolved._trackedAmbientProperties.Add(new TrackedAmbientPropertyInfo(source, sourceProp)); } } if (highestSetSource != null) { Debug.Assert(highestSetResolved != null); highestSetSource.AddPreConstructProperty(a.AmbientPropertyInfo.SettablePropertyInfo, highestSetResolved, valueCollector); } else { AddPostBuildProperty(a.AmbientPropertyInfo.SettablePropertyInfo, resolved, valueCollector); } #endregion } else { RootGeneralization.AddPreConstructProperty(a.AmbientPropertyInfo.SettablePropertyInfo, value, valueCollector); } } } }