//----------------------------------------------------------------------------- // 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 : Location { Func getMethod; Action setMethod; int expressionId; IList locationReferences; IList locations; ActivityInstance rootInstance; Activity compiledRootActivity; byte[] compiledRootActivityQualifiedId; Activity expressionActivity; byte[] expressionActivityQualifiedId; string expressionText; bool forImplementation; public CompiledLocation(Func getMethod, Action setMethod, IList locationReferences, IList 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 getMethod, Action 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> locationReferenceCache { get { if (this.locationReferences == null || this.locationReferences.Count == 0) { return null; } List> durableCache = new List>(this.locationReferences.Count); foreach (LocationReference reference in locationReferences) { durableCache.Add(new Tuple(reference.Name, reference.Type)); } return durableCache; } set { if (value == null || value.Count == 0) { this.locationReferences = new List(); return; } this.locationReferences = new List(value.Count); foreach (Tuple 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 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 tempLocation = (CompiledLocation)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)); } } } }