|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Expressions
{
using System;
using System.Activities.XamlIntegration;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Xaml;
public class CompiledExpressionInvoker
{
private static readonly AttachableMemberIdentifier compiledExpressionRootProperty =
new AttachableMemberIdentifier(typeof(CompiledExpressionInvoker), "CompiledExpressionRoot");
private static readonly AttachableMemberIdentifier compiledExpressionRootForImplementationProperty =
new AttachableMemberIdentifier(typeof(CompiledExpressionInvoker), "CompiledExpressionRootForImplementation");
int expressionId;
Activity expressionActivity;
bool isReference;
ITextExpression textExpression;
Activity metadataRoot;
ICompiledExpressionRoot compiledRoot;
IList<LocationReference> locationReferences;
CodeActivityMetadata metadata;
CodeActivityPublicEnvironmentAccessor accessor;
public bool IsStaticallyCompiled
{
get;
private set;
}
public CompiledExpressionInvoker(ITextExpression expression, bool isReference, CodeActivityMetadata metadata)
{
if (expression == null)
{
throw FxTrace.Exception.ArgumentNull("expression");
}
if (metadata == null)
{
throw FxTrace.Exception.ArgumentNull("metadata");
}
this.expressionId = -1;
this.textExpression = expression;
this.expressionActivity = expression as Activity;
this.isReference = isReference;
this.locationReferences = new List<LocationReference>();
this.metadata = metadata;
this.accessor = CodeActivityPublicEnvironmentAccessor.Create(this.metadata);
if (this.expressionActivity == null)
{
throw FxTrace.Exception.Argument("expression", SR.ITextExpressionParameterMustBeActivity);
}
ActivityWithResult resultActivity = this.expressionActivity as ActivityWithResult;
this.metadataRoot = metadata.Environment.Root;
this.ProcessLocationReferences();
}
public object InvokeExpression(ActivityContext activityContext)
{
if (activityContext == null)
{
throw FxTrace.Exception.ArgumentNull("activityContext");
}
if (this.compiledRoot == null || this.expressionId < 0)
{
if (!TryGetCompiledExpressionRoot(this.expressionActivity, this.metadataRoot, out this.compiledRoot) ||
!CanExecuteExpression(this.compiledRoot, out expressionId))
{
if (!TryGetCurrentCompiledExpressionRoot(activityContext, out this.compiledRoot, out this.expressionId))
{
throw FxTrace.Exception.AsError(new NotSupportedException(SR.TextExpressionMetadataRequiresCompilation(this.expressionActivity.GetType().Name)));
}
}
}
return this.compiledRoot.InvokeExpression(this.expressionId, this.locationReferences, activityContext);
}
//
// Attached property setter for the compiled expression root for the public surface area of an activity
public static void SetCompiledExpressionRoot(object target, ICompiledExpressionRoot compiledExpressionRoot)
{
if (compiledExpressionRoot == null)
{
AttachablePropertyServices.RemoveProperty(target, compiledExpressionRootProperty);
}
else
{
AttachablePropertyServices.SetProperty(target, compiledExpressionRootProperty, compiledExpressionRoot);
}
}
//
// Attached property getter for the compiled expression root for the public surface area of an activity
public static object GetCompiledExpressionRoot(object target)
{
object value = null;
AttachablePropertyServices.TryGetProperty(target, compiledExpressionRootProperty, out value);
return value;
}
//
// Attached property setter for the compiled expression root for the implementation surface area of an activity
public static void SetCompiledExpressionRootForImplementation(object target, ICompiledExpressionRoot compiledExpressionRoot)
{
if (compiledExpressionRoot == null)
{
AttachablePropertyServices.RemoveProperty(target, compiledExpressionRootForImplementationProperty);
}
else
{
AttachablePropertyServices.SetProperty(target, compiledExpressionRootForImplementationProperty, compiledExpressionRoot);
}
}
//
// Attached property getter for the compiled expression root for the implementation surface area of an activity
public static object GetCompiledExpressionRootForImplementation(object target)
{
object value = null;
AttachablePropertyServices.TryGetProperty(target, compiledExpressionRootForImplementationProperty, out value);
return value;
}
//
// Internal helper to find the correct ICER for a given expression.
internal static bool TryGetCompiledExpressionRoot(Activity expression, Activity target, out ICompiledExpressionRoot compiledExpressionRoot)
{
bool forImplementation = expression.MemberOf != expression.RootActivity.MemberOf;
return TryGetCompiledExpressionRoot(target, forImplementation, out compiledExpressionRoot);
}
//
// Helper to find the correct ICER for a given expression.
// This is separate from the above because within this class we switch forImplementation for the same target Activity
// to matched the ICER model of using one ICER for all expressions in the implementation and root argument defaults.
internal static bool TryGetCompiledExpressionRoot(Activity target, bool forImplementation, out ICompiledExpressionRoot compiledExpressionRoot)
{
if (!forImplementation)
{
compiledExpressionRoot = GetCompiledExpressionRoot(target) as ICompiledExpressionRoot;
if (compiledExpressionRoot != null)
{
return true;
}
//
// Default expressions for Arguments show up in the public surface area
// If we didn't find an ICER for the public surface area continue
// and try to use the implementation ICER
}
if (target is ICompiledExpressionRoot)
{
compiledExpressionRoot = (ICompiledExpressionRoot)target;
return true;
}
compiledExpressionRoot = GetCompiledExpressionRootForImplementation(target) as ICompiledExpressionRoot;
if (compiledExpressionRoot != null)
{
return true;
}
compiledExpressionRoot = null;
return false;
}
internal Expression GetExpressionTree()
{
if (this.compiledRoot == null || this.expressionId < 0)
{
if (!TryGetCompiledExpressionRootAtDesignTime(this.expressionActivity, this.metadataRoot, out this.compiledRoot, out this.expressionId))
{
return null;
}
}
return this.compiledRoot.GetExpressionTreeForExpression(this.expressionId, this.locationReferences);
}
bool TryGetCurrentCompiledExpressionRoot(ActivityContext activityContext, out ICompiledExpressionRoot compiledExpressionRoot, out int expressionId)
{
ActivityInstance current = activityContext.CurrentInstance;
while (current != null && current.Activity != this.metadataRoot)
{
ICompiledExpressionRoot currentCompiledExpressionRoot = null;
if (CompiledExpressionInvoker.TryGetCompiledExpressionRoot(current.Activity, true, out currentCompiledExpressionRoot))
{
if (CanExecuteExpression(currentCompiledExpressionRoot, out expressionId))
{
compiledExpressionRoot = currentCompiledExpressionRoot;
return true;
}
}
current = current.Parent;
}
compiledExpressionRoot = null;
expressionId = -1;
return false;
}
bool CanExecuteExpression(ICompiledExpressionRoot compiledExpressionRoot, out int expressionId)
{
if (compiledExpressionRoot.CanExecuteExpression(this.textExpression.ExpressionText, this.isReference, locationReferences, out expressionId))
{
return true;
}
return false;
}
void ProcessLocationReferences()
{
Stack<LocationReferenceEnvironment> environments = new Stack<LocationReferenceEnvironment>();
//
// Build list of location by enumerating environments
// in top down order to match the traversal pattern of TextExpressionCompiler
LocationReferenceEnvironment current = this.accessor.ActivityMetadata.Environment;
while (current != null)
{
environments.Push(current);
current = current.Parent;
}
foreach (LocationReferenceEnvironment environment in environments)
{
foreach (LocationReference reference in environment.GetLocationReferences())
{
if (this.textExpression.RequiresCompilation)
{
this.accessor.CreateLocationArgument(reference, false);
}
this.locationReferences.Add(new InlinedLocationReference(reference, this.metadata.CurrentActivity));
}
}
// Scenarios like VBV/R needs to know if they should run their own compiler
// during CacheMetadata. If we find a compiled expression root, means we're
// already compiled. So set the IsStaticallyCompiled flag to true
bool foundCompiledExpressionRoot = this.TryGetCompiledExpressionRootAtDesignTime(this.expressionActivity,
this.metadataRoot,
out this.compiledRoot,
out this.expressionId);
if (foundCompiledExpressionRoot)
{
this.IsStaticallyCompiled = true;
// For compiled C# expressions we create temp auto generated arguments
// for all locations whether they are used in the expressions or not.
// The TryGetReferenceToPublicLocation method call above also generates
// temp arguments for all locations.
// However for VB expressions, this leads to inconsistency between build
// time and run time as during build time VB only generates temp arguments
// for locations that are referenced in the expressions. To maintain
// consistency the we call the CreateRequiredArguments method seperately to
// generates auto arguments only for locations that are referenced.
if (!this.textExpression.RequiresCompilation)
{
IList<string> requiredLocationNames = this.compiledRoot.GetRequiredLocations(this.expressionId);
this.CreateRequiredArguments(requiredLocationNames);
}
}
}
bool TryGetCompiledExpressionRootAtDesignTime(Activity expression, Activity target, out ICompiledExpressionRoot compiledExpressionRoot, out int exprId)
{
exprId = -1;
compiledExpressionRoot = null;
if (!CompiledExpressionInvoker.TryGetCompiledExpressionRoot(expression, target, out compiledExpressionRoot) ||
!CanExecuteExpression(compiledExpressionRoot, out exprId))
{
return FindCompiledExpressionRoot(out exprId, out compiledExpressionRoot);
}
return true;
}
bool FindCompiledExpressionRoot(out int exprId, out ICompiledExpressionRoot compiledExpressionRoot)
{
Activity root = this.metadata.CurrentActivity.Parent;
while (root != null)
{
ICompiledExpressionRoot currentCompiledExpressionRoot = null;
if (CompiledExpressionInvoker.TryGetCompiledExpressionRoot(metadata.CurrentActivity, root, out currentCompiledExpressionRoot))
{
if (CanExecuteExpression(currentCompiledExpressionRoot, out exprId))
{
compiledExpressionRoot = currentCompiledExpressionRoot;
return true;
}
}
root = root.Parent;
}
exprId = -1;
compiledExpressionRoot = null;
return false;
}
void CreateRequiredArguments(IList<string> requiredLocationNames)
{
LocationReference reference;
if (requiredLocationNames != null && requiredLocationNames.Count > 0)
{
foreach (string name in requiredLocationNames)
{
reference = FindLocationReference(name);
if (reference != null)
{
if (this.isReference)
{
this.accessor.CreateLocationArgument(reference, true);
}
else
{
this.accessor.CreateArgument(reference, ArgumentDirection.In, true);
}
}
}
}
}
LocationReference FindLocationReference(string name)
{
LocationReference returnValue = null;
LocationReferenceEnvironment current = this.accessor.ActivityMetadata.Environment;
while (current != null)
{
if (current.TryGetLocationReference(name, out returnValue))
{
return returnValue;
}
current = current.Parent;
}
return returnValue;
}
}
}
|