e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
311 lines
11 KiB
C#
311 lines
11 KiB
C#
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities.XamlIntegration
|
|
{
|
|
using System;
|
|
using System.Activities;
|
|
using System.Activities.Expressions;
|
|
using System.Activities.Runtime;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Reflection;
|
|
using System.Collections.Generic;
|
|
|
|
[DataContract(Name = XD.CompiledLocation.Name, Namespace = XD.Runtime.Namespace)]
|
|
internal class CompiledLocation<T> : Location<T>
|
|
{
|
|
Func<T> getMethod;
|
|
Action<T> setMethod;
|
|
|
|
int expressionId;
|
|
|
|
IList<LocationReference> locationReferences;
|
|
|
|
IList<Location> locations;
|
|
|
|
ActivityInstance rootInstance;
|
|
|
|
Activity compiledRootActivity;
|
|
byte[] compiledRootActivityQualifiedId;
|
|
|
|
Activity expressionActivity;
|
|
byte[] expressionActivityQualifiedId;
|
|
|
|
string expressionText;
|
|
|
|
bool forImplementation;
|
|
|
|
public CompiledLocation(Func<T> getMethod, Action<T> setMethod, IList<LocationReference> locationReferences, IList<Location> locations, int expressionId, Activity compiledRootActivity, ActivityContext currentActivityContext)
|
|
{
|
|
this.getMethod = getMethod;
|
|
this.setMethod = setMethod;
|
|
|
|
this.forImplementation = currentActivityContext.Activity.MemberOf != currentActivityContext.Activity.RootActivity.MemberOf;
|
|
this.locationReferences = locationReferences;
|
|
this.locations = locations;
|
|
this.expressionId = expressionId;
|
|
|
|
this.compiledRootActivity = compiledRootActivity;
|
|
this.expressionActivity = currentActivityContext.Activity;
|
|
//
|
|
// Save the root activity instance to get the root activity post persistence
|
|
// The root will always be alive as long as the location is valid, which is not
|
|
// true for the activity instance of the expression that is executing
|
|
this.rootInstance = currentActivityContext.CurrentInstance;
|
|
while (this.rootInstance.Parent != null)
|
|
{
|
|
this.rootInstance = this.rootInstance.Parent;
|
|
}
|
|
//
|
|
// Save the text of the expression for exception message
|
|
ITextExpression textExpression = currentActivityContext.Activity as ITextExpression;
|
|
if (textExpression != null)
|
|
{
|
|
this.expressionText = textExpression.ExpressionText;
|
|
}
|
|
}
|
|
|
|
public CompiledLocation(Func<T> getMethod, Action<T> setMethod)
|
|
{
|
|
//
|
|
// This is the constructor that is used to refresh the get/set methods during rehydration
|
|
// An instance of this class created with the constructor cannot be invoked.
|
|
this.getMethod = getMethod;
|
|
this.setMethod = setMethod;
|
|
}
|
|
|
|
public override T Value
|
|
{
|
|
get
|
|
{
|
|
if (this.getMethod == null)
|
|
{
|
|
RefreshAccessors();
|
|
}
|
|
return getMethod();
|
|
}
|
|
set
|
|
{
|
|
if (this.setMethod == null)
|
|
{
|
|
RefreshAccessors();
|
|
}
|
|
setMethod(value);
|
|
}
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false)]
|
|
public byte[] CompiledRootActivityQualifiedId
|
|
{
|
|
get
|
|
{
|
|
if (this.compiledRootActivityQualifiedId == null)
|
|
{
|
|
return this.compiledRootActivity.QualifiedId.AsByteArray();
|
|
}
|
|
|
|
return this.compiledRootActivityQualifiedId;
|
|
}
|
|
set
|
|
{
|
|
this.compiledRootActivityQualifiedId = value;
|
|
}
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false)]
|
|
public byte[] ExpressionActivityQualifiedId
|
|
{
|
|
get
|
|
{
|
|
if (this.expressionActivityQualifiedId == null)
|
|
{
|
|
return this.expressionActivity.QualifiedId.AsByteArray();
|
|
}
|
|
|
|
return this.expressionActivityQualifiedId;
|
|
}
|
|
set
|
|
{
|
|
this.expressionActivityQualifiedId = value;
|
|
}
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false)]
|
|
public IList<Tuple<string, Type>> locationReferenceCache
|
|
{
|
|
get
|
|
{
|
|
if (this.locationReferences == null || this.locationReferences.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
List<Tuple<string, Type>> durableCache = new List<Tuple<string, Type>>(this.locationReferences.Count);
|
|
|
|
foreach (LocationReference reference in locationReferences)
|
|
{
|
|
durableCache.Add(new Tuple<string, Type>(reference.Name, reference.Type));
|
|
}
|
|
|
|
return durableCache;
|
|
}
|
|
set
|
|
{
|
|
if (value == null || value.Count == 0)
|
|
{
|
|
this.locationReferences = new List<LocationReference>();
|
|
return;
|
|
}
|
|
|
|
this.locationReferences = new List<LocationReference>(value.Count);
|
|
foreach (Tuple<string, Type> reference in value)
|
|
{
|
|
this.locationReferences.Add(new CompiledLocationReference(reference.Item1, reference.Item2));
|
|
}
|
|
}
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "expressionId")]
|
|
internal int SerializedExpressionId
|
|
{
|
|
get { return this.expressionId; }
|
|
set { this.expressionId = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "locations")]
|
|
internal IList<Location> SerializedLocations
|
|
{
|
|
get { return this.locations; }
|
|
set { this.locations = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "rootInstance")]
|
|
internal ActivityInstance SerializedRootInstance
|
|
{
|
|
get { return this.rootInstance; }
|
|
set { this.rootInstance = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "expressionText")]
|
|
internal string SerializedExpressionText
|
|
{
|
|
get { return this.expressionText; }
|
|
set { this.expressionText = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "forImplementation")]
|
|
internal bool SerializedForImplementation
|
|
{
|
|
get { return this.forImplementation; }
|
|
set { this.forImplementation = value; }
|
|
}
|
|
|
|
void RefreshAccessors()
|
|
{
|
|
//
|
|
// If we've gotten here is means that we have a location that has roundtripped through persistence
|
|
// CompiledDataContext & ICER don't round trip so we need to get them back from the current tree
|
|
// and get new pointers to the get/set methods for this expression
|
|
ICompiledExpressionRoot compiledRoot = GetCompiledExpressionRoot();
|
|
CompiledLocation<T> tempLocation = (CompiledLocation<T>)compiledRoot.InvokeExpression(this.expressionId, this.locations);
|
|
this.getMethod = tempLocation.getMethod;
|
|
this.setMethod = tempLocation.setMethod;
|
|
}
|
|
|
|
ICompiledExpressionRoot GetCompiledExpressionRoot()
|
|
{
|
|
if (this.rootInstance != null && this.rootInstance.Activity != null)
|
|
{
|
|
ICompiledExpressionRoot compiledExpressionRoot;
|
|
Activity rootActivity = this.rootInstance.Activity;
|
|
|
|
Activity compiledRootActivity = null;
|
|
Activity expressionActivity = null;
|
|
|
|
if (QualifiedId.TryGetElementFromRoot(rootActivity, this.compiledRootActivityQualifiedId, out compiledRootActivity) &&
|
|
QualifiedId.TryGetElementFromRoot(rootActivity, this.expressionActivityQualifiedId, out expressionActivity))
|
|
{
|
|
if (CompiledExpressionInvoker.TryGetCompiledExpressionRoot(expressionActivity, compiledRootActivity, out compiledExpressionRoot))
|
|
{
|
|
//
|
|
// Revalidate to make sure we didn't hit an ID shift
|
|
if (compiledExpressionRoot.CanExecuteExpression(this.expressionText, true /* this is always a reference */, this.locationReferences, out this.expressionId))
|
|
{
|
|
return compiledExpressionRoot;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// We were valid when this location was generated so an ID shift occurred (likely due to a dynamic update)
|
|
// Need to search all of the ICERs for one that can execute this expression.
|
|
if (FindCompiledExpressionRoot(rootActivity, out compiledExpressionRoot))
|
|
{
|
|
return compiledExpressionRoot;
|
|
}
|
|
}
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.UnableToLocateCompiledLocationContext(this.expressionText)));
|
|
}
|
|
|
|
bool FindCompiledExpressionRoot(Activity activity, out ICompiledExpressionRoot compiledExpressionRoot)
|
|
{
|
|
if (CompiledExpressionInvoker.TryGetCompiledExpressionRoot(activity, this.forImplementation, out compiledExpressionRoot))
|
|
{
|
|
if (compiledExpressionRoot.CanExecuteExpression(this.expressionText, true /* this is always a reference */, this.locationReferences, out this.expressionId))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
foreach (Activity containedActivity in WorkflowInspectionServices.GetActivities(activity))
|
|
{
|
|
if (FindCompiledExpressionRoot(containedActivity, out compiledExpressionRoot))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
compiledExpressionRoot = null;
|
|
return false;
|
|
}
|
|
|
|
class CompiledLocationReference : LocationReference
|
|
{
|
|
string name;
|
|
Type type;
|
|
|
|
public CompiledLocationReference(string name, Type type)
|
|
{
|
|
this.name = name;
|
|
this.type = type;
|
|
}
|
|
|
|
protected override string NameCore
|
|
{
|
|
get
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
|
|
protected override Type TypeCore
|
|
{
|
|
get
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
|
|
public override Location GetLocation(ActivityContext context)
|
|
{
|
|
//
|
|
// We should never hit this, these references are strictly for preserving location names/types
|
|
// through persistence to allow for revalidation on the other side
|
|
// Actual execution occurs through the locations that were stored separately
|
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CompiledLocationReferenceGetLocation));
|
|
}
|
|
}
|
|
}
|
|
}
|