e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1483 lines
61 KiB
C#
1483 lines
61 KiB
C#
//-----------------------------------------------------------------------------
|
|
// 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<Type> collectionInterfaces;
|
|
static Type inArgumentOfObjectType = typeof(InArgument<object>);
|
|
static Type outArgumentOfObjectType = typeof(OutArgument<object>);
|
|
static Type inOutArgumentOfObjectType = typeof(InOutArgument<object>);
|
|
static PropertyChangedEventArgs propertyChangedEventArgs;
|
|
|
|
// Can't delay create this one because we use object.ReferenceEquals on it in WorkflowInstance
|
|
static ReadOnlyDictionaryInternal<string, object> emptyParameters = new ReadOnlyDictionaryInternal<string, object>(new Dictionary<string, object>(0));
|
|
|
|
public static ReadOnlyDictionaryInternal<string, object> EmptyParameters
|
|
{
|
|
get
|
|
{
|
|
return emptyParameters;
|
|
}
|
|
}
|
|
|
|
internal static PropertyChangedEventArgs ValuePropertyChangedEventArgs
|
|
{
|
|
get
|
|
{
|
|
if (propertyChangedEventArgs == null)
|
|
{
|
|
propertyChangedEventArgs = new PropertyChangedEventArgs("Value");
|
|
}
|
|
return propertyChangedEventArgs;
|
|
}
|
|
}
|
|
|
|
static IList<Type> CollectionInterfaces
|
|
{
|
|
get
|
|
{
|
|
if (collectionInterfaces == null)
|
|
{
|
|
collectionInterfaces = new List<Type>(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<T>
|
|
argumentReference = Activator.CreateInstance(argumentValueGenericType.MakeGenericType(argumentType), referencedArgumentName);
|
|
}
|
|
else
|
|
{
|
|
// If it is InOut or Out we need an ArgumentReference<T>
|
|
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<Activity> children, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> 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<RuntimeArgument> arguments, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> 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<Variable> variables, ActivityCollectionType collectionType, bool addChildren, ref ActivityLocationReferenceEnvironment environment, ref int nextEnvironmentId, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> 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<ActivityDelegate> delegates, ActivityCollectionType collectionType, bool addChildren, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining, ref IList<ValidationError> 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<ChildActivity> activitiesRemaining, ActivityCallStack parentChain, ref IList<ValidationError> 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<Constraint> constraints = activity.RuntimeConstraints;
|
|
IList<ValidationError> 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<ValidationError>();
|
|
}
|
|
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<ValidationError> 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<ValidationError> 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<ValidationError> 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<ValidationError> 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<ChildActivity> 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<Activity> children, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
|
|
{
|
|
for (int i = 0; i < children.Count; i++)
|
|
{
|
|
SetupForProcessing(children[i], canBeExecuted, ref nextActivity, ref activitiesRemaining);
|
|
}
|
|
}
|
|
|
|
static void SetupForProcessing(IList<ActivityDelegate> delegates, bool canBeExecuted, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
|
|
{
|
|
for (int i = 0; i < delegates.Count; i++)
|
|
{
|
|
SetupForProcessing(delegates[i], canBeExecuted, ref nextActivity, ref activitiesRemaining);
|
|
}
|
|
}
|
|
|
|
static void SetupForProcessing(IList<Variable> variables, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
|
|
{
|
|
for (int i = 0; i < variables.Count; i++)
|
|
{
|
|
SetupForProcessing(variables[i], ref nextActivity, ref activitiesRemaining);
|
|
}
|
|
}
|
|
|
|
static void SetupForProcessing(IList<RuntimeArgument> arguments, ref ChildActivity nextActivity, ref Stack<ChildActivity> 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<ChildActivity> activitiesRemaining)
|
|
{
|
|
if (activityDelegate.Handler != null)
|
|
{
|
|
SetupForProcessing(activityDelegate.Handler, canBeExecuted, ref nextActivity, ref activitiesRemaining);
|
|
}
|
|
}
|
|
|
|
static void SetupForProcessing(Variable variable, ref ChildActivity nextActivity, ref Stack<ChildActivity> activitiesRemaining)
|
|
{
|
|
if (variable.Default != null)
|
|
{
|
|
SetupForProcessing(variable.Default, true, ref nextActivity, ref activitiesRemaining);
|
|
}
|
|
}
|
|
|
|
static void SetupForProcessing(RuntimeArgument argument, ref ChildActivity nextActivity, ref Stack<ChildActivity> 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<ChildActivity> activitiesRemaining)
|
|
{
|
|
if (!nextActivity.Equals(ChildActivity.Empty))
|
|
{
|
|
if (activitiesRemaining == null)
|
|
{
|
|
activitiesRemaining = new Stack<ChildActivity>();
|
|
}
|
|
|
|
activitiesRemaining.Push(nextActivity);
|
|
}
|
|
|
|
nextActivity = new ChildActivity(activity, canBeExecuted);
|
|
}
|
|
|
|
public static void ProcessActivityInstanceTree(ActivityInstance rootInstance, ActivityExecutor executor, Func<ActivityInstance, ActivityExecutor, bool> callback)
|
|
{
|
|
Queue<IList<ActivityInstance>> 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<TResult>(CompletionCallback<TResult> onCompleted, ActivityInstance owningInstance)
|
|
{
|
|
if (onCompleted != null)
|
|
{
|
|
return new CompletionBookmark(new FuncCompletionCallbackWrapper<TResult>(onCompleted, owningInstance));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string GetTraceString(Bookmark bookmark)
|
|
{
|
|
if (bookmark.IsNamed)
|
|
{
|
|
return "'" + bookmark.Name + "'";
|
|
}
|
|
else
|
|
{
|
|
return string.Format(CultureInfo.InvariantCulture, "<Unnamed Id={0}>", bookmark.Id);
|
|
}
|
|
}
|
|
|
|
public static string GetTraceString(BookmarkScope bookmarkScope)
|
|
{
|
|
if (bookmarkScope == null)
|
|
{
|
|
return "<None>";
|
|
}
|
|
else if (bookmarkScope.IsInitialized)
|
|
{
|
|
return "'" + bookmarkScope.Id.ToString() + "'";
|
|
}
|
|
else
|
|
{
|
|
return string.Format(CultureInfo.InvariantCulture, "<Uninitialized TemporaryId={0}>", 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<T>(ref Collection<T> collection, T data)
|
|
{
|
|
if (data != null)
|
|
{
|
|
if (collection == null)
|
|
{
|
|
collection = new Collection<T>();
|
|
}
|
|
collection.Add(data);
|
|
}
|
|
}
|
|
|
|
public static void Add<T>(ref IList<T> list, T data)
|
|
{
|
|
if (data != null)
|
|
{
|
|
if (list == null)
|
|
{
|
|
list = new List<T>();
|
|
}
|
|
list.Add(data);
|
|
}
|
|
}
|
|
|
|
public class TreeProcessingList
|
|
{
|
|
ActivityInstance singleItem;
|
|
IList<ActivityInstance> 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<ActivityInstance> 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<ActivityInstance>(this.multipleItems);
|
|
this.addRequiresNewList = false;
|
|
}
|
|
|
|
this.multipleItems.Add(item);
|
|
}
|
|
else if (this.singleItem != null)
|
|
{
|
|
this.multipleItems = new List<ActivityInstance>(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<ChildActivity>
|
|
{
|
|
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<ChildActivity> callStack;
|
|
|
|
public ActivityCallStack()
|
|
{
|
|
callStack = new Quack<ChildActivity>();
|
|
}
|
|
|
|
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<Type, ILocationReferenceExpression> locationReferenceValueTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
|
|
|
|
static object environmentLocationReferenceTypeDefinitionsLock = new object();
|
|
static Dictionary<Type, ILocationReferenceExpression> environmentLocationReferenceTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
|
|
|
|
static object environmentLocationValueTypeDefinitionsLock = new object();
|
|
static Dictionary<Type, ILocationReferenceExpression> environmentLocationValueTypeDefinitions = new Dictionary<Type, ILocationReferenceExpression>();
|
|
|
|
public static ActivityWithResult CreateNewLocationAccessExpression(Type type, bool isReference, bool useLocationReferenceValue, LocationReference locationReference)
|
|
{
|
|
Dictionary<Type, ILocationReferenceExpression> 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);
|
|
}
|
|
}
|
|
}
|
|
}
|