//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Activities { using System; using System.Activities.Expressions; using System.Activities.Runtime; using System.Activities.Validation; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Reflection; using System.Runtime; using System.Text; static class ActivityUtilities { static Pop popActivity = new Pop(); static Type activityType = typeof(Activity); static Type activityGenericType = typeof(Activity<>); static Type activityDelegateType = typeof(ActivityDelegate); static Type constraintType = typeof(Constraint); static Type variableType = typeof(Variable); static Type variableGenericType = typeof(Variable<>); static Type delegateInArgumentType = typeof(DelegateInArgument); static Type delegateOutArgumentType = typeof(DelegateOutArgument); static Type delegateInArgumentGenericType = typeof(DelegateInArgument<>); static Type delegateOutArgumentGenericType = typeof(DelegateOutArgument<>); static Type inArgumentType = typeof(InArgument); static Type inArgumentGenericType = typeof(InArgument<>); static Type inOutArgumentType = typeof(InOutArgument); static Type inOutArgumentGenericType = typeof(InOutArgument<>); static Type outArgumentType = typeof(OutArgument); static Type outArgumentGenericType = typeof(OutArgument<>); static Type argumentType = typeof(Argument); static Type argumentReferenceGenericType = typeof(ArgumentReference<>); static Type argumentValueGenericType = typeof(ArgumentValue<>); static Type runtimeArgumentType = typeof(RuntimeArgument); static Type locationGenericType = typeof(Location<>); static Type variableReferenceGenericType = typeof(VariableReference<>); static Type variableValueGenericType = typeof(VariableValue<>); static Type delegateArgumentValueGenericType = typeof(DelegateArgumentValue<>); static Type handleType = typeof(Handle); static Type iDictionaryGenericType = typeof(IDictionary<,>); static Type locationReferenceValueType = typeof(LocationReferenceValue<>); static Type environmentLocationValueType = typeof(EnvironmentLocationValue<>); static Type environmentLocationReferenceType = typeof(EnvironmentLocationReference<>); static IList collectionInterfaces; static Type inArgumentOfObjectType = typeof(InArgument); static Type outArgumentOfObjectType = typeof(OutArgument); static Type inOutArgumentOfObjectType = typeof(InOutArgument); static PropertyChangedEventArgs propertyChangedEventArgs; // Can't delay create this one because we use object.ReferenceEquals on it in WorkflowInstance static ReadOnlyDictionaryInternal emptyParameters = new ReadOnlyDictionaryInternal(new Dictionary(0)); public static ReadOnlyDictionaryInternal EmptyParameters { get { return emptyParameters; } } internal static PropertyChangedEventArgs ValuePropertyChangedEventArgs { get { if (propertyChangedEventArgs == null) { propertyChangedEventArgs = new PropertyChangedEventArgs("Value"); } return propertyChangedEventArgs; } } static IList CollectionInterfaces { get { if (collectionInterfaces == null) { collectionInterfaces = new List(2) { typeof(IList<>), typeof(ICollection<>) }; } return collectionInterfaces; } } public static bool IsInScope(ActivityInstance potentialChild, ActivityInstance scope) { if (scope == null) { // No scope means we're in scope return true; } ActivityInstance walker = potentialChild; while (walker != null && walker != scope) { walker = walker.Parent; } return walker != null; } public static bool IsHandle(Type type) { return handleType.IsAssignableFrom(type); } public static bool IsCompletedState(ActivityInstanceState state) { return state != ActivityInstanceState.Executing; } public static bool TryGetArgumentDirectionAndType(Type propertyType, out ArgumentDirection direction, out Type argumentType) { direction = ArgumentDirection.In; // default to In argumentType = TypeHelper.ObjectType; // default to object if (propertyType.IsGenericType) { argumentType = propertyType.GetGenericArguments()[0]; Type genericType = propertyType.GetGenericTypeDefinition(); if (genericType == inArgumentGenericType) { return true; } if (genericType == outArgumentGenericType) { direction = ArgumentDirection.Out; return true; } if (genericType == inOutArgumentGenericType) { direction = ArgumentDirection.InOut; return true; } } else { if (propertyType == inArgumentType) { return true; } if (propertyType == outArgumentType) { direction = ArgumentDirection.Out; return true; } if (propertyType == inOutArgumentType) { direction = ArgumentDirection.InOut; return true; } } return false; } public static bool IsArgumentType(Type propertyType) { return TypeHelper.AreTypesCompatible(propertyType, argumentType); } public static bool IsRuntimeArgumentType(Type propertyType) { return TypeHelper.AreTypesCompatible(propertyType, runtimeArgumentType); } public static bool IsArgumentDictionaryType(Type type, out Type innerType) { if (type.IsGenericType) { bool implementsIDictionary = false; Type dictionaryInterfaceType = null; if (type.GetGenericTypeDefinition() == iDictionaryGenericType) { implementsIDictionary = true; dictionaryInterfaceType = type; } else { foreach (Type interfaceType in type.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == iDictionaryGenericType) { implementsIDictionary = true; dictionaryInterfaceType = interfaceType; break; } } } if (implementsIDictionary == true) { Type[] genericArguments = dictionaryInterfaceType.GetGenericArguments(); if (genericArguments[0] == TypeHelper.StringType && IsArgumentType(genericArguments[1])) { innerType = genericArguments[1]; return true; } } } innerType = null; return false; } public static bool IsKnownCollectionType(Type type, out Type innerType) { if (type.IsGenericType) { if (type.IsInterface) { Type localInterface = type.GetGenericTypeDefinition(); foreach (Type knownInterface in CollectionInterfaces) { if (localInterface == knownInterface) { Type[] genericArguments = type.GetGenericArguments(); if (genericArguments.Length == 1) { innerType = genericArguments[0]; return true; } } } } else { // Ask the type whether or not it implements any known collections. Type[] interfaceTypes = type.GetInterfaces(); foreach (Type interfaceType in interfaceTypes) { if (interfaceType.IsGenericType) { Type localInterface = interfaceType.GetGenericTypeDefinition(); foreach (Type knownInterface in CollectionInterfaces) { if (localInterface == knownInterface) { Type[] genericArguments = interfaceType.GetGenericArguments(); if (genericArguments.Length == 1) { innerType = genericArguments[0]; return true; } } } } } } } innerType = null; return false; } public static bool IsActivityDelegateType(Type propertyType) { return TypeHelper.AreTypesCompatible(propertyType, activityDelegateType); } public static bool IsActivityType(Type propertyType) { return IsActivityType(propertyType, true); } public static bool IsActivityType(Type propertyType, bool includeConstraints) { if (!TypeHelper.AreTypesCompatible(propertyType, activityType)) { return false; } // sometimes (for reflection analysis of Activity properties) we don't want constraints to count return includeConstraints || !TypeHelper.AreTypesCompatible(propertyType, constraintType); } public static bool TryGetDelegateArgumentDirectionAndType(Type propertyType, out ArgumentDirection direction, out Type argumentType) { direction = ArgumentDirection.In; // default to In argumentType = TypeHelper.ObjectType; // default to object if (propertyType.IsGenericType) { argumentType = propertyType.GetGenericArguments()[0]; Type genericType = propertyType.GetGenericTypeDefinition(); if (genericType == delegateInArgumentGenericType) { return true; } if (genericType == delegateOutArgumentGenericType) { direction = ArgumentDirection.Out; return true; } } else { if (propertyType == delegateInArgumentType) { return true; } if (propertyType == delegateOutArgumentType) { direction = ArgumentDirection.Out; return true; } } return false; } public static bool IsVariableType(Type propertyType, out Type innerType) { if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == variableGenericType) { innerType = propertyType.GetGenericArguments()[0]; return true; } innerType = null; return TypeHelper.AreTypesCompatible(propertyType, variableType); } public static bool IsVariableType(Type propertyType) { if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == variableGenericType) { return true; } return TypeHelper.AreTypesCompatible(propertyType, variableType); } public static bool IsLocationGenericType(Type type, out Type genericArgumentType) { if (type.IsGenericType && type.GetGenericTypeDefinition() == locationGenericType) { genericArgumentType = type.GetGenericArguments()[0]; return true; } genericArgumentType = null; return false; } public static object CreateVariableReference(Variable variable) { Type genericVariableReferenceType = variableReferenceGenericType.MakeGenericType(variable.Type); object variableReference = Activator.CreateInstance(genericVariableReferenceType); genericVariableReferenceType.GetProperty("Variable").SetValue(variableReference, variable, null); return variableReference; } public static ActivityWithResult CreateLocationAccessExpression(LocationReference locationReference, bool isReference, bool useLocationReferenceValue) { return LocationAccessExpressionTypeDefinitionsCache.CreateNewLocationAccessExpression(locationReference.Type, isReference, useLocationReferenceValue, locationReference); } public static Argument CreateArgument(Type type, ArgumentDirection direction) { Type argumentType = ArgumentTypeDefinitionsCache.GetArgumentType(type, direction); Argument argument = (Argument)Activator.CreateInstance(argumentType); return argument; } public static Argument CreateArgumentOfObject(ArgumentDirection direction) { Argument argument = null; if (direction == ArgumentDirection.In) { argument = (Argument)Activator.CreateInstance(inArgumentOfObjectType); } else if (direction == ArgumentDirection.Out) { argument = (Argument)Activator.CreateInstance(outArgumentOfObjectType); } else { argument = (Argument)Activator.CreateInstance(inOutArgumentOfObjectType); } return argument; } public static Type CreateLocation(Type locationType) { return locationGenericType.MakeGenericType(locationType); } public static Type CreateActivityWithResult(Type resultType) { return activityGenericType.MakeGenericType(resultType); } public static Argument CreateReferenceArgument(Type argumentType, ArgumentDirection direction, string referencedArgumentName) { Argument argument = Argument.Create(argumentType, direction); object argumentReference = null; if (direction == ArgumentDirection.In) { // If it is an In then we need an ArgumentValue argumentReference = Activator.CreateInstance(argumentValueGenericType.MakeGenericType(argumentType), referencedArgumentName); } else { // If it is InOut or Out we need an ArgumentReference argumentReference = Activator.CreateInstance(argumentReferenceGenericType.MakeGenericType(argumentType), referencedArgumentName); } argument.Expression = (ActivityWithResult)argumentReference; return argument; } public static Variable CreateVariable(string name, Type type, VariableModifiers modifiers) { Type variableType = variableGenericType.MakeGenericType(type); Variable variable = (Variable)Activator.CreateInstance(variableType); variable.Name = name; variable.Modifiers = modifiers; return variable; } // The argumentConsumer is the activity that is attempting to reference the argument // with argumentName. That means that argumentConsumer must be in the Implementation // of an activity that defines an argument with argumentName. public static RuntimeArgument FindArgument(string argumentName, Activity argumentConsumer) { if (argumentConsumer.MemberOf != null && argumentConsumer.MemberOf.Owner != null) { Activity targetActivity = argumentConsumer.MemberOf.Owner; for (int i = 0; i < targetActivity.RuntimeArguments.Count; i++) { RuntimeArgument argument = targetActivity.RuntimeArguments[i]; if (argument.Name == argumentName) { return argument; } } } return null; } public static string GetDisplayName(object source) { Fx.Assert(source != null, "caller must verify"); return GetDisplayName(source.GetType()); } static string GetDisplayName(Type sourceType) { if (sourceType.IsGenericType) { // start with the type name string displayName = sourceType.Name; int tickIndex = displayName.IndexOf('`'); // remove the tick+number of parameters "generics format". Note that the // tick won't exist for nested implicitly generic classes, such as Foo`1+Bar if (tickIndex > 0) { displayName = displayName.Substring(0, tickIndex); } // and provide a more readable version based on the closure type names Type[] genericArguments = sourceType.GetGenericArguments(); StringBuilder stringBuilder = new StringBuilder(displayName); stringBuilder.Append("<"); for (int i = 0; i < genericArguments.Length - 1; i++) { stringBuilder.AppendFormat("{0},", GetDisplayName(genericArguments[i])); } stringBuilder.AppendFormat("{0}>", GetDisplayName(genericArguments[genericArguments.Length - 1])); return stringBuilder.ToString(); } else { Fx.Assert(!sourceType.IsGenericTypeDefinition, "we have an actual object, so we should never have a generic type definition"); return sourceType.Name; } } internal static void ValidateOrigin(object origin, Activity activity) { if (origin != null && (origin is Activity || origin is Argument || origin is ActivityDelegate || origin is LocationReference)) { activity.AddTempValidationError(new ValidationError(SR.OriginCannotBeRuntimeIntrinsic(origin))); } } // Returns true if there are any children static void ProcessChildren(Activity parent, IList children, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack activitiesRemaining, ref IList validationErrors) { for (int i = 0; i < children.Count; i++) { Activity childActivity = children[i]; if (childActivity.InitializeRelationship(parent, collectionType, ref validationErrors)) { if (addChildren) { SetupForProcessing(childActivity, collectionType != ActivityCollectionType.Imports, ref nextActivity, ref activitiesRemaining); } } } } // Note that we do not need an "isPublicCollection" parameter since all arguments are public // Returns true if there are any non-null expressions static void ProcessArguments(Activity parent, IList arguments, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack activitiesRemaining, ref IList validationErrors) { if (arguments.Count > 0) { if (environment == null) { environment = new ActivityLocationReferenceEnvironment(parent.GetParentEnvironment()); } for (int i = 0; i < arguments.Count; i++) { RuntimeArgument argument = arguments[i]; if (argument.InitializeRelationship(parent, ref validationErrors)) { argument.Id = nextEnvironmentId; nextEnvironmentId++; // This must be called after InitializeRelationship since it makes // use of RuntimeArgument.Owner; environment.Declare(argument, argument.Owner, ref validationErrors); if (addChildren) { SetupForProcessing(argument, ref nextActivity, ref activitiesRemaining); } } } } } // Returns true if there are any non-null defaults static void ProcessVariables(Activity parent, IList variables, ActivityCollectionType collectionType, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack activitiesRemaining, ref IList validationErrors) { if (variables.Count > 0) { if (environment == null) { environment = new ActivityLocationReferenceEnvironment(parent.GetParentEnvironment()); } for (int i = 0; i < variables.Count; i++) { Variable variable = variables[i]; if (variable.InitializeRelationship(parent, collectionType == ActivityCollectionType.Public, ref validationErrors)) { variable.Id = nextEnvironmentId; nextEnvironmentId++; // This must be called after InitializeRelationship since it makes // use of Variable.Owner; environment.Declare(variable, variable.Owner, ref validationErrors); if (addChildren) { SetupForProcessing(variable, ref nextActivity, ref activitiesRemaining); } } } } } // Returns true if there are any non-null handlers static void ProcessDelegates(Activity parent, IList delegates, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack activitiesRemaining, ref IList validationErrors) { for (int i = 0; i < delegates.Count; i++) { ActivityDelegate activityDelegate = delegates[i]; if (activityDelegate.InitializeRelationship(parent, collectionType, ref validationErrors)) { if (addChildren) { SetupForProcessing(activityDelegate, collectionType != ActivityCollectionType.Imports, ref nextActivity, ref activitiesRemaining); } } } } static void ProcessActivity(ChildActivity childActivity, ref ChildActivity nextActivity, ref Stack activitiesRemaining, ActivityCallStack parentChain, ref IList validationErrors, ProcessActivityTreeOptions options, ProcessActivityCallback callback) { Fx.Assert(options != null, "options should not be null."); if (options.CancellationToken.IsCancellationRequested) { throw FxTrace.Exception.AsError(new OperationCanceledException(options.CancellationToken)); } Activity activity = childActivity.Activity; IList constraints = activity.RuntimeConstraints; IList tempValidationErrors = null; Fx.Assert(validationErrors == null || !options.StoreTempViolations, "Incoming violations should be null if we are storing them in Activity.tempViolations."); if (!activity.HasStartedCachingMetadata) { // We need to add this activity to the IdSpace first so that we have a meaningful ID // for any errors that may occur. Fx.Assert(activity.MemberOf != null, "We always set this ahead of time - the root is set in InitializeAsRoot and all others are set in InitializeRelationship."); activity.MemberOf.AddMember(activity); if (TD.InternalCacheMetadataStartIsEnabled()) { TD.InternalCacheMetadataStart(activity.Id); } activity.InternalCacheMetadata(options.CreateEmptyBindings, ref tempValidationErrors); if (TD.InternalCacheMetadataStopIsEnabled()) { TD.InternalCacheMetadataStop(activity.Id); } ActivityValidationServices.ValidateArguments(activity, activity.Parent == null, ref tempValidationErrors); ActivityLocationReferenceEnvironment newPublicEnvironment = null; ActivityLocationReferenceEnvironment newImplementationEnvironment = new ActivityLocationReferenceEnvironment(activity.HostEnvironment) { InternalRoot = activity }; int nextEnvironmentId = 0; ProcessChildren(activity, activity.Children, ActivityCollectionType.Public, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessChildren(activity, activity.ImportedChildren, ActivityCollectionType.Imports, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessChildren(activity, activity.ImplementationChildren, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessArguments(activity, activity.RuntimeArguments, true, ref newImplementationEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessVariables(activity, activity.RuntimeVariables, ActivityCollectionType.Public, true, ref newPublicEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessVariables(activity, activity.ImplementationVariables, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref newImplementationEnvironment, ref nextEnvironmentId, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); if (activity.HandlerOf != null) { // Since we are a delegate handler we have to do some processing // of the handlers parameters. This is the one part of the tree // walk that actually reaches _up_ to process something we've // already passed. for (int i = 0; i < activity.HandlerOf.RuntimeDelegateArguments.Count; i++) { RuntimeDelegateArgument delegateArgument = activity.HandlerOf.RuntimeDelegateArguments[i]; DelegateArgument boundArgument = delegateArgument.BoundArgument; if (boundArgument != null) { // At runtime, delegate arguments end up owned by the Handler // and are scoped like public variables of the handler. // // And since they don't own an expression, there's no equivalent // SetupForProcessing method for DelegateArguments if (boundArgument.InitializeRelationship(activity, ref tempValidationErrors)) { boundArgument.Id = nextEnvironmentId; nextEnvironmentId++; } } } } // NOTE: At this point the declared environment is complete (either we're using the parent or we've got a new one) if (newPublicEnvironment == null) { activity.PublicEnvironment = new ActivityLocationReferenceEnvironment(activity.GetParentEnvironment()); } else { if (newPublicEnvironment.Parent == null) { newPublicEnvironment.InternalRoot = activity; } activity.PublicEnvironment = newPublicEnvironment; } activity.ImplementationEnvironment = newImplementationEnvironment; // ProcessDelegates uses activity.Environment ProcessDelegates(activity, activity.Delegates, ActivityCollectionType.Public, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessDelegates(activity, activity.ImportedDelegates, ActivityCollectionType.Imports, true, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); ProcessDelegates(activity, activity.ImplementationDelegates, ActivityCollectionType.Implementation, !options.SkipPrivateChildren, ref nextActivity, ref activitiesRemaining, ref tempValidationErrors); if (callback != null) { callback(childActivity, parentChain); } // copy validation errors in ValidationErrors list if (tempValidationErrors != null) { if (validationErrors == null) { validationErrors = new List(); } Activity source; string prefix = ActivityValidationServices.GenerateValidationErrorPrefix(childActivity.Activity, parentChain, options, out source); for (int i = 0; i < tempValidationErrors.Count; i++) { ValidationError validationError = tempValidationErrors[i]; validationError.Source = source; validationError.Id = source.Id; if (!string.IsNullOrEmpty(prefix)) { validationError.Message = prefix + validationError.Message; } validationErrors.Add(validationError); } tempValidationErrors = null; } if (options.StoreTempViolations) { if (validationErrors != null) { childActivity.Activity.SetTempValidationErrorCollection(validationErrors); validationErrors = null; } } } else { // We're processing a reference // Add all the children for processing even though they've already // been seen. SetupForProcessing(activity.Children, true, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.ImportedChildren, false, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.RuntimeArguments, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.RuntimeVariables, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.Delegates, true, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.ImportedDelegates, false, ref nextActivity, ref activitiesRemaining); if (!options.SkipPrivateChildren) { SetupForProcessing(activity.ImplementationChildren, true, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.ImplementationDelegates, true, ref nextActivity, ref activitiesRemaining); SetupForProcessing(activity.ImplementationVariables, ref nextActivity, ref activitiesRemaining); } if (callback != null && !options.OnlyCallCallbackForDeclarations) { callback(childActivity, parentChain); } if (childActivity.Activity.HasTempViolations && !options.StoreTempViolations) { childActivity.Activity.TransferTempValidationErrors(ref validationErrors); } } // We only run constraints if the activity could possibly // execute and we aren't explicitly skipping them. if (!options.SkipConstraints && parentChain.WillExecute && childActivity.CanBeExecuted && constraints.Count > 0) { ActivityValidationServices.RunConstraints(childActivity, parentChain, constraints, options, false, ref validationErrors); } } // We explicitly call this CacheRootMetadata since it treats the provided // activity as the root of the tree. public static void CacheRootMetadata(Activity activity, LocationReferenceEnvironment hostEnvironment, ProcessActivityTreeOptions options, ProcessActivityCallback callback, ref IList validationErrors) { if (TD.CacheRootMetadataStartIsEnabled()) { TD.CacheRootMetadataStart(activity.DisplayName); } if (!ShouldShortcut(activity, options)) { lock (activity.ThisLock) { if (!ShouldShortcut(activity, options)) { if (activity.HasBeenAssociatedWithAnInstance) { throw FxTrace.Exception.AsError(new InvalidOperationException(SR.RootActivityAlreadyAssociatedWithInstance(activity.DisplayName))); } activity.InitializeAsRoot(hostEnvironment); ProcessActivityTreeCore(new ChildActivity(activity, true), null, options, callback, ref validationErrors); // Regardless of where the violations came from we only want to // set ourselves RuntimeReady if there are no errors and are // fully cached. if (!ActivityValidationServices.HasErrors(validationErrors) && options.IsRuntimeReadyOptions) { // We don't really support progressive caching at runtime so we only set ourselves // as runtime ready if we cached the whole workflow and created empty bindings. // In order to support progressive caching we need to deal with the following // issues: // * We need a mechanism for supporting activities which supply extensions // * We need to understand when we haven't created empty bindings so that // we can progressively create them // * We need a mechanism for making sure that we've validated parent related // constraints at all possible callsites activity.SetRuntimeReady(); } } } } if (TD.CacheRootMetadataStopIsEnabled()) { TD.CacheRootMetadataStop(activity.DisplayName); } } // This API is only valid from ProcessActivityCallbacks. It will cache the rest of the subtree rooted at the // provided activity allowing inspection of child metadata before the normal caching pass hits it. public static void FinishCachingSubtree(ChildActivity subtreeRoot, ActivityCallStack parentChain, ProcessActivityTreeOptions options) { IList discardedValidationErrors = null; ProcessActivityTreeCore(subtreeRoot, parentChain, ProcessActivityTreeOptions.GetFinishCachingSubtreeOptions(options), new ProcessActivityCallback(NoOpCallback), ref discardedValidationErrors); } public static void FinishCachingSubtree(ChildActivity subtreeRoot, ActivityCallStack parentChain, ProcessActivityTreeOptions options, ProcessActivityCallback callback) { IList discardedValidationErrors = null; ProcessActivityTreeCore(subtreeRoot, parentChain, ProcessActivityTreeOptions.GetFinishCachingSubtreeOptions(options), callback, ref discardedValidationErrors); } static void NoOpCallback(ChildActivity element, ActivityCallStack parentChain) { } static bool ShouldShortcut(Activity activity, ProcessActivityTreeOptions options) { if (options.SkipIfCached && options.IsRuntimeReadyOptions) { return activity.IsRuntimeReady; } return false; } static void ProcessActivityTreeCore(ChildActivity currentActivity, ActivityCallStack parentChain, ProcessActivityTreeOptions options, ProcessActivityCallback callback, ref IList validationErrors) { Fx.Assert(options != null, "We need you to explicitly specify options."); Fx.Assert(currentActivity.Activity.MemberOf != null, "We must have an activity with MemberOf setup or we need to skipIdGeneration."); ChildActivity nextActivity = ChildActivity.Empty; Stack activitiesRemaining = null; if (parentChain == null) { parentChain = new ActivityCallStack(); } if (options.OnlyVisitSingleLevel) { ProcessActivity(currentActivity, ref nextActivity, ref activitiesRemaining, parentChain, ref validationErrors, options, callback); } else { while (!currentActivity.Equals(ChildActivity.Empty)) { if (object.ReferenceEquals(currentActivity.Activity, popActivity)) { ChildActivity completedParent = parentChain.Pop(); completedParent.Activity.SetCached(isSkippingPrivateChildren: options.SkipPrivateChildren); } else { SetupForProcessing(popActivity, true, ref nextActivity, ref activitiesRemaining); ProcessActivity(currentActivity, ref nextActivity, ref activitiesRemaining, parentChain, ref validationErrors, options, callback); parentChain.Push(currentActivity); } // nextActivity is the top of the stack // stackTop => nextActivity => currentActivity currentActivity = nextActivity; if (activitiesRemaining != null && activitiesRemaining.Count > 0) { nextActivity = activitiesRemaining.Pop(); } else { nextActivity = ChildActivity.Empty; } } } } static void SetupForProcessing(IList children, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { for (int i = 0; i < children.Count; i++) { SetupForProcessing(children[i], canBeExecuted, ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(IList delegates, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { for (int i = 0; i < delegates.Count; i++) { SetupForProcessing(delegates[i], canBeExecuted, ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(IList variables, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { for (int i = 0; i < variables.Count; i++) { SetupForProcessing(variables[i], ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(IList arguments, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { for (int i = 0; i < arguments.Count; i++) { SetupForProcessing(arguments[i], ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(ActivityDelegate activityDelegate, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { if (activityDelegate.Handler != null) { SetupForProcessing(activityDelegate.Handler, canBeExecuted, ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(Variable variable, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { if (variable.Default != null) { SetupForProcessing(variable.Default, true, ref nextActivity, ref activitiesRemaining); } } static void SetupForProcessing(RuntimeArgument argument, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { if (argument.BoundArgument != null && !argument.BoundArgument.IsEmpty) { SetupForProcessing(argument.BoundArgument.Expression, true, ref nextActivity, ref activitiesRemaining); } } // nextActivity is always the top of the stack static void SetupForProcessing(Activity activity, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack activitiesRemaining) { if (!nextActivity.Equals(ChildActivity.Empty)) { if (activitiesRemaining == null) { activitiesRemaining = new Stack(); } activitiesRemaining.Push(nextActivity); } nextActivity = new ChildActivity(activity, canBeExecuted); } public static void ProcessActivityInstanceTree(ActivityInstance rootInstance, ActivityExecutor executor, Func callback) { Queue> instancesRemaining = null; TreeProcessingList currentInstancesList = new TreeProcessingList(); currentInstancesList.Add(rootInstance); TreeProcessingList nextInstanceList = null; if (rootInstance.HasChildren) { nextInstanceList = new TreeProcessingList(); } while ((instancesRemaining != null && instancesRemaining.Count > 0) || currentInstancesList.Count != 0) { if (currentInstancesList.Count == 0) { Fx.Assert(instancesRemaining != null && instancesRemaining.Count > 0, "This must be the clause that caused us to enter"); currentInstancesList.Set(instancesRemaining.Dequeue()); } for (int i = 0; i < currentInstancesList.Count; i++) { ActivityInstance instance = currentInstancesList[i]; if (callback(instance, executor) && instance.HasChildren) { Fx.Assert(nextInstanceList != null, "We should have created this list if we are going to get here."); instance.AppendChildren(nextInstanceList, ref instancesRemaining); } } if (nextInstanceList != null && nextInstanceList.Count > 0) { nextInstanceList.TransferTo(currentInstancesList); } else { // We'll just reuse this object on the next pass (Set will be called) currentInstancesList.Reset(); } } } public delegate void ProcessActivityCallback(ChildActivity childActivity, ActivityCallStack parentChain); public static FaultBookmark CreateFaultBookmark(FaultCallback onFaulted, ActivityInstance owningInstance) { if (onFaulted != null) { return new FaultBookmark(new FaultCallbackWrapper(onFaulted, owningInstance)); } return null; } public static CompletionBookmark CreateCompletionBookmark(CompletionCallback onCompleted, ActivityInstance owningInstance) { if (onCompleted != null) { return new CompletionBookmark(new ActivityCompletionCallbackWrapper(onCompleted, owningInstance)); } return null; } public static CompletionBookmark CreateCompletionBookmark(DelegateCompletionCallback onCompleted, ActivityInstance owningInstance) { if (onCompleted != null) { return new CompletionBookmark(new DelegateCompletionCallbackWrapper(onCompleted, owningInstance)); } return null; } public static CompletionBookmark CreateCompletionBookmark(CompletionCallback onCompleted, ActivityInstance owningInstance) { if (onCompleted != null) { return new CompletionBookmark(new FuncCompletionCallbackWrapper(onCompleted, owningInstance)); } return null; } public static string GetTraceString(Bookmark bookmark) { if (bookmark.IsNamed) { return "'" + bookmark.Name + "'"; } else { return string.Format(CultureInfo.InvariantCulture, "", bookmark.Id); } } public static string GetTraceString(BookmarkScope bookmarkScope) { if (bookmarkScope == null) { return ""; } else if (bookmarkScope.IsInitialized) { return "'" + bookmarkScope.Id.ToString() + "'"; } else { return string.Format(CultureInfo.InvariantCulture, "", bookmarkScope.TemporaryId); } } public static void RemoveNulls(IList list) { if (list != null) { for (int i = list.Count - 1; i >= 0; i--) { if (list[i] == null) { list.RemoveAt(i); } } } } public static void Add(ref Collection collection, T data) { if (data != null) { if (collection == null) { collection = new Collection(); } collection.Add(data); } } public static void Add(ref IList list, T data) { if (data != null) { if (list == null) { list = new List(); } list.Add(data); } } public class TreeProcessingList { ActivityInstance singleItem; IList multipleItems; bool addRequiresNewList; public TreeProcessingList() { } public int Count { get { if (this.singleItem != null) { return 1; } if (this.multipleItems != null) { return this.multipleItems.Count; } return 0; } } public ActivityInstance this[int index] { get { if (this.singleItem != null) { Fx.Assert(index == 0, "We expect users of TreeProcessingList never to be out of range."); return this.singleItem; } else { Fx.Assert(this.multipleItems != null, "Users shouldn't call this if we have no items."); Fx.Assert(this.multipleItems.Count > index, "Users should never be out of range."); return this.multipleItems[index]; } } } public void Set(IList listToSet) { Fx.Assert(singleItem == null && (this.multipleItems == null || this.multipleItems.Count == 0), "We should not have any items if calling set."); this.multipleItems = listToSet; this.addRequiresNewList = true; } public void Add(ActivityInstance item) { if (this.multipleItems != null) { if (this.addRequiresNewList) { this.multipleItems = new List(this.multipleItems); this.addRequiresNewList = false; } this.multipleItems.Add(item); } else if (this.singleItem != null) { this.multipleItems = new List(2); this.multipleItems.Add(this.singleItem); this.multipleItems.Add(item); this.singleItem = null; } else { this.singleItem = item; } } // Because of how we use this we don't need a Clear(). // Basically we gain nothing by clearing the multipleItems // list and hanging onto it. public void Reset() { this.addRequiresNewList = false; this.multipleItems = null; this.singleItem = null; } public void TransferTo(TreeProcessingList otherList) { otherList.singleItem = this.singleItem; otherList.multipleItems = this.multipleItems; otherList.addRequiresNewList = this.addRequiresNewList; Reset(); } } // We don't implement anything in this class. We just use it as // a placeholder for when to pop off our parent stack. class Pop : Activity { internal override void InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) { throw Fx.AssertAndThrow("should never get here"); } internal override void OnInternalCacheMetadata(bool createEmptyBindings) { throw Fx.AssertAndThrow("should never get here"); } } public struct ChildActivity : IEquatable { public ChildActivity(Activity activity, bool canBeExecuted) : this() { Activity = activity; CanBeExecuted = canBeExecuted; } public static ChildActivity Empty { get { return new ChildActivity(); } } public Activity Activity { get; set; } public bool CanBeExecuted { get; set; } public bool Equals(ChildActivity other) { return object.ReferenceEquals(Activity, other.Activity) && CanBeExecuted == other.CanBeExecuted; } } public class ActivityCallStack { int nonExecutingParentCount; Quack callStack; public ActivityCallStack() { callStack = new Quack(); } public bool WillExecute { get { return nonExecutingParentCount == 0; } } public ChildActivity this[int index] { get { return this.callStack[index]; } } public int Count { get { return this.callStack.Count; } } public void Push(ChildActivity childActivity) { if (!childActivity.CanBeExecuted) { this.nonExecutingParentCount++; } this.callStack.PushFront(childActivity); } public ChildActivity Pop() { ChildActivity childActivity = this.callStack.Dequeue(); if (!childActivity.CanBeExecuted) { this.nonExecutingParentCount--; } return childActivity; } } static class ArgumentTypeDefinitionsCache { static Hashtable inArgumentTypeDefinitions = new Hashtable(); static Hashtable outArgumentTypeDefinitions = new Hashtable(); static Hashtable inOutArgumentTypeDefinitions = new Hashtable(); public static Type GetArgumentType(Type type, ArgumentDirection direction) { Hashtable lookupTable = null; if (direction == ArgumentDirection.In) { lookupTable = inArgumentTypeDefinitions; } else if (direction == ArgumentDirection.Out) { lookupTable = outArgumentTypeDefinitions; } else { lookupTable = inOutArgumentTypeDefinitions; } Type argumentType = lookupTable[type] as Type; if (argumentType == null) { argumentType = CreateArgumentType(type, direction); lock (lookupTable) { lookupTable[type] = argumentType; } } return argumentType; } static Type CreateArgumentType(Type type, ArgumentDirection direction) { Type argumentType = null; if (direction == ArgumentDirection.In) { argumentType = ActivityUtilities.inArgumentGenericType.MakeGenericType(type); } else if (direction == ArgumentDirection.Out) { argumentType = ActivityUtilities.outArgumentGenericType.MakeGenericType(type); } else { argumentType = ActivityUtilities.inOutArgumentGenericType.MakeGenericType(type); } return argumentType; } } static class LocationAccessExpressionTypeDefinitionsCache { static object locationReferenceValueTypeDefinitionsLock = new object(); static Dictionary locationReferenceValueTypeDefinitions = new Dictionary(); static object environmentLocationReferenceTypeDefinitionsLock = new object(); static Dictionary environmentLocationReferenceTypeDefinitions = new Dictionary(); static object environmentLocationValueTypeDefinitionsLock = new object(); static Dictionary environmentLocationValueTypeDefinitions = new Dictionary(); public static ActivityWithResult CreateNewLocationAccessExpression(Type type, bool isReference, bool useLocationReferenceValue, LocationReference locationReference) { Dictionary lookupTable = null; object tableLock = null; if (useLocationReferenceValue) { lookupTable = locationReferenceValueTypeDefinitions; tableLock = locationReferenceValueTypeDefinitionsLock; } else { lookupTable = isReference ? environmentLocationReferenceTypeDefinitions : environmentLocationValueTypeDefinitions; tableLock = isReference ? environmentLocationReferenceTypeDefinitionsLock : environmentLocationValueTypeDefinitionsLock; } ILocationReferenceExpression existingInstance; lock (tableLock) { if (!lookupTable.TryGetValue(type, out existingInstance)) { Type locationAccessExpressionType = CreateLocationAccessExpressionType(type, isReference, useLocationReferenceValue); // Create an "empty" (locationReference = null) instance to put in the cache. This empty instance will only be used to create other instances, // including the instance returned from this method. The cached instance will never be included in an activity tree, so the cached instance's // rootActivity field will not be filled in and thus will not pin all the objects in the activity tree. The cached empty instance has a null // locationReference because locationReference also pins parts of activity tree. existingInstance = (ILocationReferenceExpression)Activator.CreateInstance( locationAccessExpressionType, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { null }, null); lookupTable[type] = existingInstance; } } return existingInstance.CreateNewInstance(locationReference); } static Type CreateLocationAccessExpressionType(Type type, bool isReference, bool useLocationReferenceValue) { Type openType; if (useLocationReferenceValue) { openType = locationReferenceValueType; } else { openType = isReference ? environmentLocationReferenceType : environmentLocationValueType; } return openType.MakeGenericType(type); } } } }