|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace Microsoft.VisualBasic.Activities
{
using System;
using System.Activities;
using System.Activities.ExpressionParser;
using System.Activities.Expressions;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime;
using System.Runtime.Collections;
using System.Text;
using System.Threading;
using Microsoft.Compiler.VisualBasic;
using System.Collections.ObjectModel;
using Microsoft.VisualBasic.CompilerServices;
using System.Security;
using System.Security.Permissions;
class VisualBasicHelper
{
internal static string Language
{
get
{
return "VB";
}
}
// the following assemblies are provided to the compiler by default
// items are public so the decompiler knows which assemblies it doesn't need to reference for interfaces
public static readonly HashSet<Assembly> DefaultReferencedAssemblies = new HashSet<Assembly>
{
typeof(int).Assembly, // mscorlib
typeof(CodeTypeDeclaration).Assembly, // System
typeof(Expression).Assembly, // System.Core
typeof(Microsoft.VisualBasic.Strings).Assembly, // Microsoft.VisualBasic
typeof(Activity).Assembly // System.Activities
};
public static AssemblyName GetFastAssemblyName(Assembly assembly)
{
return AssemblyReference.GetFastAssemblyName(assembly);
}
// cache for type's all base types, interfaces, generic arguments, element type
// HopperCache is a psuedo-MRU cache
const int typeReferenceCacheMaxSize = 100;
static object typeReferenceCacheLock = new object();
static HopperCache typeReferenceCache = new HopperCache(typeReferenceCacheMaxSize, false);
static ulong lastTimestamp = 0;
// Cache<(expressionText+ReturnType+Assemblies+Imports), LambdaExpression>
// LambdaExpression represents raw ExpressionTrees right out of the vb hosted compiler
// these raw trees are yet to be rewritten with appropriate Variables
const int rawTreeCacheMaxSize = 128;
static object rawTreeCacheLock = new object();
[Fx.Tag.SecurityNote(Critical = "Critical because it caches objects created under a demand for FullTrust.")]
[SecurityCritical]
static HopperCache rawTreeCache;
static HopperCache RawTreeCache
{
[Fx.Tag.SecurityNote(Critical = "Critical because it access critical member rawTreeCache.")]
[SecurityCritical]
get
{
if (rawTreeCache == null)
{
rawTreeCache = new HopperCache(rawTreeCacheMaxSize, false);
}
return rawTreeCache;
}
}
const int HostedCompilerCacheSize = 10;
[Fx.Tag.SecurityNote(Critical = "Critical because it holds HostedCompilerWrappers which hold HostedCompiler instances, which require FullTrust.")]
[SecurityCritical]
static Dictionary<HashSet<Assembly>, HostedCompilerWrapper> HostedCompilerCache;
[Fx.Tag.SecurityNote(Critical = "Critical because it creates Microsoft.Compiler.VisualBasic.HostedCompiler, which is in a non-APTCA assembly, and thus has a LinkDemand.",
Safe = "Safe because it puts the HostedCompiler instance into the HostedCompilerCache member, which is SecurityCritical and we are demanding FullTrust.")]
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
static HostedCompilerWrapper GetCachedHostedCompiler(HashSet<Assembly> assemblySet)
{
if (HostedCompilerCache == null)
{
// we don't want to newup a Dictionary everytime GetCachedHostedCompiler is called only to find out the cache is already initialized.
Interlocked.CompareExchange(ref HostedCompilerCache,
new Dictionary<HashSet<Assembly>, HostedCompilerWrapper>(HostedCompilerCacheSize, HashSet<Assembly>.CreateSetComparer()),
null);
}
lock (HostedCompilerCache)
{
HostedCompilerWrapper hcompilerWrapper;
if (HostedCompilerCache.TryGetValue(assemblySet, out hcompilerWrapper))
{
hcompilerWrapper.Reserve(unchecked(++VisualBasicHelper.lastTimestamp));
return hcompilerWrapper;
}
if (HostedCompilerCache.Count >= HostedCompilerCacheSize)
{
// Find oldest used compiler to kick out
ulong oldestTimestamp = ulong.MaxValue;
HashSet<Assembly> oldestCompiler = null;
foreach (KeyValuePair<HashSet<Assembly>, HostedCompilerWrapper> kvp in HostedCompilerCache)
{
if (oldestTimestamp > kvp.Value.Timestamp)
{
oldestCompiler = kvp.Key;
oldestTimestamp = kvp.Value.Timestamp;
}
}
if (oldestCompiler != null)
{
hcompilerWrapper = HostedCompilerCache[oldestCompiler];
HostedCompilerCache.Remove(oldestCompiler);
hcompilerWrapper.MarkAsKickedOut();
}
}
hcompilerWrapper = new HostedCompilerWrapper(new HostedCompiler(assemblySet.ToList()));
HostedCompilerCache[assemblySet] = hcompilerWrapper;
hcompilerWrapper.Reserve(unchecked(++VisualBasicHelper.lastTimestamp));
return hcompilerWrapper;
}
}
string textToCompile;
HashSet<Assembly> referencedAssemblies;
HashSet<string> namespaceImports;
LocationReferenceEnvironment environment;
CodeActivityPublicEnvironmentAccessor? publicAccessor;
// this is a flag to differentiate the cached short-cut Rewrite from the normal post-compilation Rewrite
bool isShortCutRewrite = false;
public VisualBasicHelper(string expressionText, HashSet<AssemblyName> refAssemNames, HashSet<string> namespaceImportsNames)
: this(expressionText)
{
Initialize(refAssemNames, namespaceImportsNames);
}
VisualBasicHelper(string expressionText)
{
this.textToCompile = expressionText;
}
public string TextToCompile { get { return this.textToCompile; } }
void Initialize(HashSet<AssemblyName> refAssemNames, HashSet<string> namespaceImportsNames)
{
this.namespaceImports = namespaceImportsNames;
foreach (AssemblyName assemblyName in refAssemNames)
{
if (this.referencedAssemblies == null)
{
this.referencedAssemblies = new HashSet<Assembly>();
}
Assembly loaded = AssemblyReference.GetAssembly(assemblyName);
if (loaded != null)
{
this.referencedAssemblies.Add(loaded);
}
}
}
public static void GetAllImportReferences(Activity activity, bool isDesignTime, out IList<string> namespaces, out IList<AssemblyReference> assemblies)
{
List<string> namespaceList = new List<string>();
List<AssemblyReference> assemblyList = new List<AssemblyReference>();
// Start with the defaults; any settings on the Activity will be added to these
// The default settings are mutable, so we need to re-copy this list on every call
ExtractNamespacesAndReferences(VisualBasicSettings.Default, namespaceList, assemblyList);
LocationReferenceEnvironment environment = activity.GetParentEnvironment();
if (environment == null || environment.Root == null)
{
namespaces = namespaceList;
assemblies = assemblyList;
return;
}
VisualBasicSettings rootVBSettings = VisualBasic.GetSettings(environment.Root);
if (rootVBSettings != null)
{
// We have VBSettings
ExtractNamespacesAndReferences(rootVBSettings, namespaceList, assemblyList);
}
else
{
// Use TextExpression settings
IList<string> rootNamespaces;
IList<AssemblyReference> rootAssemblies;
if (isDesignTime)
{
// When called via VisualBasicDesignerHelper, we don't know whether or not
// we're in an implementation, so check both.
rootNamespaces = TextExpression.GetNamespacesForImplementation(environment.Root);
rootAssemblies = TextExpression.GetReferencesForImplementation(environment.Root);
if (rootNamespaces.Count == 0 && rootAssemblies.Count == 0)
{
rootNamespaces = TextExpression.GetNamespaces(environment.Root);
rootAssemblies = TextExpression.GetReferences(environment.Root);
}
}
else
{
rootNamespaces = TextExpression.GetNamespacesInScope(activity);
rootAssemblies = TextExpression.GetReferencesInScope(activity);
}
namespaceList.AddRange(rootNamespaces);
assemblyList.AddRange(rootAssemblies);
}
namespaces = namespaceList;
assemblies = assemblyList;
}
static void ExtractNamespacesAndReferences(VisualBasicSettings vbSettings,
IList<string> namespaces, IList<AssemblyReference> assemblies)
{
foreach (VisualBasicImportReference importReference in vbSettings.ImportReferences)
{
namespaces.Add(importReference.Import);
assemblies.Add(new AssemblyReference
{
Assembly = importReference.EarlyBoundAssembly,
AssemblyName = importReference.AssemblyName
});
}
}
public static Expression<Func<ActivityContext, T>> Compile<T>(string expressionText, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression)
{
IList<string> localNamespaces;
IList<AssemblyReference> localAssemblies;
GetAllImportReferences(publicAccessor.ActivityMetadata.CurrentActivity,
false, out localNamespaces, out localAssemblies);
VisualBasicHelper helper = new VisualBasicHelper(expressionText);
HashSet<AssemblyName> localReferenceAssemblies = new HashSet<AssemblyName>();
HashSet<string> localImports = new HashSet<string>(localNamespaces);
foreach (AssemblyReference assemblyReference in localAssemblies)
{
if (assemblyReference.Assembly != null)
{
// directly add the Assembly to the list
// so that we don't have to go through
// the assembly resolution process
if (helper.referencedAssemblies == null)
{
helper.referencedAssemblies = new HashSet<Assembly>();
}
helper.referencedAssemblies.Add(assemblyReference.Assembly);
}
else if (assemblyReference.AssemblyName != null)
{
localReferenceAssemblies.Add(assemblyReference.AssemblyName);
}
}
helper.Initialize(localReferenceAssemblies, localImports);
return helper.Compile<T>(publicAccessor, isLocationExpression);
}
[Fx.Tag.SecurityNote(Critical = "Critical because it invokes a HostedCompiler, which requires FullTrust and also accesses RawTreeCache, which is SecurityCritical.",
Safe = "Safe because we are demanding FullTrust.")]
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public LambdaExpression CompileNonGeneric(LocationReferenceEnvironment environment)
{
bool abort;
Expression finalBody;
Microsoft.Compiler.VisualBasic.CompilerResults results;
this.environment = environment;
if (this.referencedAssemblies == null)
{
this.referencedAssemblies = new HashSet<Assembly>();
}
this.referencedAssemblies.UnionWith(DefaultReferencedAssemblies);
List<Import> importList = new List<Import>();
foreach (string namespaceImport in this.namespaceImports)
{
if (!String.IsNullOrEmpty(namespaceImport))
{
importList.Add(new Import(namespaceImport));
}
}
RawTreeCacheKey rawTreeKey = new RawTreeCacheKey(
this.textToCompile,
null,
this.referencedAssemblies,
this.namespaceImports);
RawTreeCacheValueWrapper rawTreeHolder = RawTreeCache.GetValue(rawTreeCacheLock, rawTreeKey) as RawTreeCacheValueWrapper;
if (rawTreeHolder != null)
{
// try short-cut
// if variable resolution fails at Rewrite, rewind and perform normal compile steps
LambdaExpression rawTree = rawTreeHolder.Value;
this.isShortCutRewrite = true;
finalBody = Rewrite(rawTree.Body, null, false, out abort);
this.isShortCutRewrite = false;
if (!abort)
{
return Expression.Lambda(rawTree.Type, finalBody, rawTree.Parameters);
}
// if we are here, then that means the shortcut Rewrite failed.
// we don't want to see too many of vb expressions in this pass since we just wasted Rewrite time for no good.
}
VisualBasicScriptAndTypeScope scriptAndTypeScope = new VisualBasicScriptAndTypeScope(
this.environment,
this.referencedAssemblies.ToList<Assembly>());
IImportScope importScope = new VisualBasicImportScope(importList);
CompilerOptions options = new CompilerOptions();
options.OptionStrict = OptionStrictSetting.On;
CompilerContext context = new CompilerContext(scriptAndTypeScope, scriptAndTypeScope, importScope, options);
HostedCompilerWrapper compilerWrapper = GetCachedHostedCompiler(this.referencedAssemblies);
HostedCompiler compiler = compilerWrapper.Compiler;
try
{
lock (compiler)
{
try
{
results = compiler.CompileExpression(this.textToCompile, context);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
FxTrace.Exception.TraceUnhandledException(e);
throw;
}
}
}
finally
{
compilerWrapper.Release();
}
if (scriptAndTypeScope.ErrorMessage != null)
{
throw FxTrace.Exception.AsError(new SourceExpressionException(SR.CompilerErrorSpecificExpression(textToCompile, scriptAndTypeScope.ErrorMessage)));
}
if (results.Errors != null && results.Errors.Count > 0)
{
// this expression has problems, so report them
StringBuilder errorString = new StringBuilder();
errorString.AppendLine();
foreach (Error error in results.Errors)
{
errorString.AppendLine(error.Description);
}
throw FxTrace.Exception.AsError(new SourceExpressionException(SR.CompilerErrorSpecificExpression(textToCompile, errorString.ToString())));
}
// replace the field references with variable references to our dummy variables
// and rewrite lambda.body.Type to equal the lambda return type T
LambdaExpression lambda = results.CodeBlock;
if (lambda == null)
{
// ExpressionText was either an empty string or Null
// we return null which eventually evaluates to default(TResult) at execution time.
return null;
}
// add the pre-rewrite lambda to RawTreeCache
AddToRawTreeCache(rawTreeKey, rawTreeHolder, lambda);
finalBody = Rewrite(lambda.Body, null, false, out abort);
Fx.Assert(abort == false, "this non-shortcut Rewrite must always return abort == false");
return Expression.Lambda(lambda.Type, finalBody, lambda.Parameters);
}
public Expression<Func<ActivityContext, T>> Compile<T>(CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationReference = false)
{
this.publicAccessor = publicAccessor;
return Compile<T>(publicAccessor.ActivityMetadata.Environment, isLocationReference);
}
// Soft-Link: This method is called through reflection by VisualBasicDesignerHelper.
public Expression<Func<ActivityContext, T>> Compile<T>(LocationReferenceEnvironment environment)
{
Fx.Assert(this.publicAccessor == null, "No public accessor so the value for isLocationReference doesn't matter");
return Compile<T>(environment, false);
}
[Fx.Tag.SecurityNote(Critical = "Critical because it invokes a HostedCompiler, which requires FullTrust and also accesses RawTreeCache, which is SecurityCritical.",
Safe = "Safe because we are demanding FullTrust.")]
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
public Expression<Func<ActivityContext, T>> Compile<T>(LocationReferenceEnvironment environment, bool isLocationReference)
{
bool abort;
Expression finalBody;
Microsoft.Compiler.VisualBasic.CompilerResults results;
Type lambdaReturnType = typeof(T);
this.environment = environment;
if (this.referencedAssemblies == null)
{
this.referencedAssemblies = new HashSet<Assembly>();
}
this.referencedAssemblies.UnionWith(DefaultReferencedAssemblies);
List<Import> importList = new List<Import>();
foreach (string namespaceImport in this.namespaceImports)
{
if (!String.IsNullOrEmpty(namespaceImport))
{
importList.Add(new Import(namespaceImport));
}
}
RawTreeCacheKey rawTreeKey = new RawTreeCacheKey(
this.textToCompile,
lambdaReturnType,
this.referencedAssemblies,
this.namespaceImports);
RawTreeCacheValueWrapper rawTreeHolder = RawTreeCache.GetValue(rawTreeCacheLock, rawTreeKey) as RawTreeCacheValueWrapper;
if (rawTreeHolder != null)
{
// try short-cut
// if variable resolution fails at Rewrite, rewind and perform normal compile steps
LambdaExpression rawTree = rawTreeHolder.Value;
this.isShortCutRewrite = true;
finalBody = Rewrite(rawTree.Body, null, isLocationReference, out abort);
this.isShortCutRewrite = false;
if (!abort)
{
Fx.Assert(finalBody.Type == lambdaReturnType, "Compiler generated ExpressionTree return type doesn't match the target return type");
// convert it into the our expected lambda format (context => ...)
return Expression.Lambda<Func<ActivityContext, T>>(finalBody,
FindParameter(finalBody) ?? ExpressionUtilities.RuntimeContextParameter);
}
// if we are here, then that means the shortcut Rewrite failed.
// we don't want to see too many of vb expressions in this pass since we just wasted Rewrite time for no good.
if (this.publicAccessor != null)
{
// from the preceding shortcut rewrite, we probably have generated tempAutoGeneratedArguments
// they are not valid anymore since we just aborted the shortcut rewrite.
// clean up, and start again.
this.publicAccessor.Value.ActivityMetadata.CurrentActivity.ResetTempAutoGeneratedArguments();
}
}
// ensure the return type's assembly is added to ref assembly list
HashSet<Type> allBaseTypes = null;
EnsureTypeReferenced(lambdaReturnType, ref allBaseTypes);
foreach (Type baseType in allBaseTypes)
{
// allBaseTypes list always contains lambdaReturnType
this.referencedAssemblies.Add(baseType.Assembly);
}
VisualBasicScriptAndTypeScope scriptAndTypeScope = new VisualBasicScriptAndTypeScope(
this.environment,
this.referencedAssemblies.ToList<Assembly>());
IImportScope importScope = new VisualBasicImportScope(importList);
CompilerOptions options = new CompilerOptions();
options.OptionStrict = OptionStrictSetting.On;
CompilerContext context = new CompilerContext(scriptAndTypeScope, scriptAndTypeScope, importScope, options);
HostedCompilerWrapper compilerWrapper = GetCachedHostedCompiler(this.referencedAssemblies);
HostedCompiler compiler = compilerWrapper.Compiler;
if (TD.CompileVbExpressionStartIsEnabled())
{
TD.CompileVbExpressionStart(this.textToCompile);
}
try
{
lock (compiler)
{
try
{
results = compiler.CompileExpression(this.textToCompile, context, lambdaReturnType);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
// We never want to end up here, Compiler bugs needs to be fixed.
FxTrace.Exception.TraceUnhandledException(e);
throw;
}
}
}
finally
{
compilerWrapper.Release();
}
if (TD.CompileVbExpressionStopIsEnabled())
{
TD.CompileVbExpressionStop();
}
if (scriptAndTypeScope.ErrorMessage != null)
{
throw FxTrace.Exception.AsError(new SourceExpressionException(SR.CompilerErrorSpecificExpression(textToCompile, scriptAndTypeScope.ErrorMessage)));
}
if (results.Errors != null && results.Errors.Count > 0)
{
// this expression has problems, so report them
StringBuilder errorString = new StringBuilder();
errorString.AppendLine();
foreach (Error error in results.Errors)
{
errorString.AppendLine(error.Description);
}
throw FxTrace.Exception.AsError(new SourceExpressionException(SR.CompilerErrorSpecificExpression(textToCompile, errorString.ToString())));
}
// replace the field references with variable references to our dummy variables
// and rewrite lambda.body.Type to equal the lambda return type T
LambdaExpression lambda = results.CodeBlock;
if (lambda == null)
{
// ExpressionText was either an empty string or Null
// we return null which eventually evaluates to default(TResult) at execution time.
return null;
}
// add the pre-rewrite lambda to RawTreeCache
AddToRawTreeCache(rawTreeKey, rawTreeHolder, lambda);
finalBody = Rewrite(lambda.Body, null, isLocationReference, out abort);
Fx.Assert(abort == false, "this non-shortcut Rewrite must always return abort == false");
Fx.Assert(finalBody.Type == lambdaReturnType, "Compiler generated ExpressionTree return type doesn't match the target return type");
// convert it into the our expected lambda format (context => ...)
return Expression.Lambda<Func<ActivityContext, T>>(finalBody,
FindParameter(finalBody) ?? ExpressionUtilities.RuntimeContextParameter);
}
[Fx.Tag.SecurityNote(Critical = "Critical because it access SecurityCritical member RawTreeCache, thus requiring FullTrust.",
Safe = "Safe because we are demanding FullTrust.")]
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
static void AddToRawTreeCache(RawTreeCacheKey rawTreeKey, RawTreeCacheValueWrapper rawTreeHolder, LambdaExpression lambda)
{
if (rawTreeHolder != null)
{
// this indicates that the key had been found in RawTreeCache,
// but the value Expression Tree failed the short-cut Rewrite.
// ---- is really not an issue here, because
// any one of possibly many raw Expression Trees that are all
// represented by the same key can be written here.
rawTreeHolder.Value = lambda;
}
else
{
// we never hit RawTreeCache with the given key
lock (rawTreeCacheLock)
{
// ensure we don't add the same key with two differnt RawTreeValueWrappers
if (RawTreeCache.GetValue(rawTreeCacheLock, rawTreeKey) == null)
{
// do we need defense against alternating miss of the shortcut Rewrite?
RawTreeCache.Add(rawTreeKey, new RawTreeCacheValueWrapper() { Value = lambda });
}
}
}
}
delegate bool FindMatch(LocationReference reference, string targetName, Type targetType, out bool terminateSearch);
static FindMatch delegateFindLocationReferenceMatchShortcut = new FindMatch(FindLocationReferenceMatchShortcut);
static FindMatch delegateFindFirstLocationReferenceMatch = new FindMatch(FindFirstLocationReferenceMatch);
static FindMatch delegateFindAllLocationReferenceMatch = new FindMatch(FindAllLocationReferenceMatch);
static bool FindLocationReferenceMatchShortcut(LocationReference reference, string targetName, Type targetType, out bool terminateSearch)
{
terminateSearch = false;
if (string.Equals(reference.Name, targetName, StringComparison.OrdinalIgnoreCase))
{
if (targetType != reference.Type)
{
terminateSearch = true;
return false;
}
return true;
}
return false;
}
static bool FindFirstLocationReferenceMatch(LocationReference reference, string targetName, Type targetType, out bool terminateSearch)
{
terminateSearch = false;
if (string.Equals(reference.Name, targetName, StringComparison.OrdinalIgnoreCase))
{
terminateSearch = true;
return true;
}
return false;
}
static bool FindAllLocationReferenceMatch(LocationReference reference, string targetName, Type targetType, out bool terminateSearch)
{
terminateSearch = false;
if (string.Equals(reference.Name, targetName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
// Returning null indicates the cached LambdaExpression used here doesn't coincide with current LocationReferenceEnvironment.
// Null return value causes the process to rewind and start from HostedCompiler.CompileExpression().
Expression Rewrite(Expression expression, ReadOnlyCollection<ParameterExpression> lambdaParameters, out bool abort)
{
return Rewrite(expression, lambdaParameters, false, out abort);
}
Expression Rewrite(Expression expression, ReadOnlyCollection<ParameterExpression> lambdaParameters, bool isLocationExpression, out bool abort)
{
int i;
int j;
Expression expr1;
Expression expr2;
Expression expr3;
List<Expression> arguments;
NewExpression newExpression;
ReadOnlyCollection<Expression> tmpArguments;
abort = false;
if (expression == null)
{
return null;
}
switch (expression.NodeType)
{
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Coalesce:
case ExpressionType.Divide:
case ExpressionType.Equal:
case ExpressionType.ExclusiveOr:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LeftShift:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.Modulo:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.NotEqual:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.Power:
case ExpressionType.RightShift:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
BinaryExpression binaryExpression = (BinaryExpression)expression;
expr1 = Rewrite(binaryExpression.Left, lambdaParameters, out abort);
if (abort)
{
return null;
}
expr2 = Rewrite(binaryExpression.Right, lambdaParameters, out abort);
if (abort)
{
return null;
}
LambdaExpression conversion = (LambdaExpression)Rewrite(binaryExpression.Conversion, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.MakeBinary(
binaryExpression.NodeType,
expr1,
expr2,
binaryExpression.IsLiftedToNull,
binaryExpression.Method,
conversion);
case ExpressionType.Conditional:
ConditionalExpression conditional = (ConditionalExpression)expression;
expr1 = Rewrite(conditional.Test, lambdaParameters, out abort);
if (abort)
{
return null;
}
expr2 = Rewrite(conditional.IfTrue, lambdaParameters, out abort);
if (abort)
{
return null;
}
expr3 = Rewrite(conditional.IfFalse, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.Condition(expr1, expr2, expr3);
case ExpressionType.Constant:
return expression;
case ExpressionType.Invoke:
InvocationExpression invocation = (InvocationExpression)expression;
expr1 = Rewrite(invocation.Expression, lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments = null;
tmpArguments = invocation.Arguments;
Fx.Assert(tmpArguments != null, "InvocationExpression.Arguments must not be null");
if (tmpArguments.Count > 0)
{
arguments = new List<Expression>(tmpArguments.Count);
for (i = 0; i < tmpArguments.Count; i++)
{
expr2 = Rewrite(tmpArguments[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments.Add(expr2);
}
}
return Expression.Invoke(expr1, arguments);
case ExpressionType.Lambda:
LambdaExpression lambda = (LambdaExpression)expression;
expr1 = Rewrite(lambda.Body, lambda.Parameters, isLocationExpression, out abort);
if (abort)
{
return null;
}
return Expression.Lambda(lambda.Type, expr1, lambda.Parameters);
case ExpressionType.ListInit:
ListInitExpression listInit = (ListInitExpression)expression;
newExpression = (NewExpression)Rewrite(listInit.NewExpression, lambdaParameters, out abort);
if (abort)
{
return null;
}
ReadOnlyCollection<ElementInit> tmpInitializers = listInit.Initializers;
Fx.Assert(tmpInitializers != null, "ListInitExpression.Initializers must not be null");
List<ElementInit> initializers = new List<ElementInit>(tmpInitializers.Count);
for (i = 0; i < tmpInitializers.Count; i++)
{
tmpArguments = tmpInitializers[i].Arguments;
Fx.Assert(tmpArguments != null, "ElementInit.Arguments must not be null");
arguments = new List<Expression>(tmpArguments.Count);
for (j = 0; j < tmpArguments.Count; j++)
{
expr1 = Rewrite(tmpArguments[j], lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments.Add(expr1);
}
initializers.Add(Expression.ElementInit(tmpInitializers[i].AddMethod, arguments));
}
return Expression.ListInit(newExpression, initializers);
case ExpressionType.Parameter:
ParameterExpression variableExpression = (ParameterExpression)expression;
{
if (lambdaParameters != null && lambdaParameters.Contains(variableExpression))
{
return variableExpression;
}
FindMatch findMatch;
if (this.isShortCutRewrite)
{
//
// this is the opportunity to inspect whether the cached LambdaExpression(raw expression tree)
// does coincide with the current LocationReferenceEnvironment.
// If any mismatch discovered, it immediately returns NULL, indicating cache lookup failure.
//
findMatch = delegateFindLocationReferenceMatchShortcut;
}
else
{
//
// variable(LocationReference) resolution process
// Note that the non-shortcut compilation pass always gaurantees successful variable resolution here.
//
findMatch = delegateFindFirstLocationReferenceMatch;
}
bool foundMultiple;
LocationReference finalReference = FindLocationReferencesFromEnvironment(
this.environment,
findMatch,
variableExpression.Name,
variableExpression.Type,
out foundMultiple);
if (finalReference != null && !foundMultiple)
{
if (this.publicAccessor != null)
{
CodeActivityPublicEnvironmentAccessor localPublicAccessor = this.publicAccessor.Value;
LocationReference inlinedReference;
if (ExpressionUtilities.TryGetInlinedReference(localPublicAccessor,
finalReference, isLocationExpression, out inlinedReference))
{
finalReference = inlinedReference;
}
}
return ExpressionUtilities.CreateIdentifierExpression(finalReference);
}
if (this.isShortCutRewrite)
{
// cached LambdaExpression doesn't match this LocationReferenceEnvironment!!
// no matching LocationReference found.
// fail immeditely.
abort = true;
return null;
}
// if we are here, this variableExpression is a temp variable
// generated by the compiler.
return variableExpression;
}
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression;
// When creating a location for a member on a struct, we also need a location
// for the struct (so we don't just set the member on a copy of the struct)
bool subTreeIsLocationExpression = isLocationExpression && memberExpression.Member.DeclaringType.IsValueType;
expr1 = Rewrite(memberExpression.Expression, lambdaParameters, subTreeIsLocationExpression, out abort);
if (abort)
{
return null;
}
return Expression.MakeMemberAccess(expr1, memberExpression.Member);
case ExpressionType.MemberInit:
MemberInitExpression memberInit = (MemberInitExpression)expression;
newExpression = (NewExpression)Rewrite(memberInit.NewExpression, lambdaParameters, out abort);
if (abort)
{
return null;
}
ReadOnlyCollection<MemberBinding> tmpMemberBindings = memberInit.Bindings;
Fx.Assert(tmpMemberBindings != null, "MemberInitExpression.Bindings must not be null");
List<MemberBinding> bindings = new List<MemberBinding>(tmpMemberBindings.Count);
for (i = 0; i < tmpMemberBindings.Count; i++)
{
MemberBinding binding = Rewrite(tmpMemberBindings[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
bindings.Add(binding);
}
return Expression.MemberInit(newExpression, bindings);
case ExpressionType.ArrayIndex:
// ArrayIndex can be a MethodCallExpression or a BinaryExpression
MethodCallExpression arrayIndex = expression as MethodCallExpression;
if (arrayIndex != null)
{
expr1 = Rewrite(arrayIndex.Object, lambdaParameters, out abort);
if (abort)
{
return null;
}
tmpArguments = arrayIndex.Arguments;
Fx.Assert(tmpArguments != null, "MethodCallExpression.Arguments must not be null");
List<Expression> indexes = new List<Expression>(tmpArguments.Count);
for (i = 0; i < tmpArguments.Count; i++)
{
expr2 = Rewrite(tmpArguments[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
indexes.Add(expr2);
}
return Expression.ArrayIndex(expr1, indexes);
}
BinaryExpression alternateIndex = (BinaryExpression)expression;
expr1 = Rewrite(alternateIndex.Left, lambdaParameters, out abort);
if (abort)
{
return null;
}
expr2 = Rewrite(alternateIndex.Right, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.ArrayIndex(expr1, expr2);
case ExpressionType.Call:
MethodCallExpression methodCall = (MethodCallExpression)expression;
expr1 = Rewrite(methodCall.Object, lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments = null;
tmpArguments = methodCall.Arguments;
Fx.Assert(tmpArguments != null, "MethodCallExpression.Arguments must not be null");
if (tmpArguments.Count > 0)
{
arguments = new List<Expression>(tmpArguments.Count);
for (i = 0; i < tmpArguments.Count; i++)
{
expr2 = Rewrite(tmpArguments[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments.Add(expr2);
}
}
return Expression.Call(expr1, methodCall.Method, arguments);
case ExpressionType.NewArrayInit:
NewArrayExpression newArray = (NewArrayExpression)expression;
ReadOnlyCollection<Expression> tmpExpressions = newArray.Expressions;
Fx.Assert(tmpExpressions != null, "NewArrayExpression.Expressions must not be null");
List<Expression> arrayInitializers = new List<Expression>(tmpExpressions.Count);
for (i = 0; i < tmpExpressions.Count; i++)
{
expr1 = Rewrite(tmpExpressions[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
arrayInitializers.Add(expr1);
}
return Expression.NewArrayInit(newArray.Type.GetElementType(), arrayInitializers);
case ExpressionType.NewArrayBounds:
NewArrayExpression newArrayBounds = (NewArrayExpression)expression;
tmpExpressions = newArrayBounds.Expressions;
Fx.Assert(tmpExpressions != null, "NewArrayExpression.Expressions must not be null");
List<Expression> bounds = new List<Expression>(tmpExpressions.Count);
for (i = 0; i < tmpExpressions.Count; i++)
{
expr1 = Rewrite(tmpExpressions[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
bounds.Add(expr1);
}
return Expression.NewArrayBounds(newArrayBounds.Type.GetElementType(), bounds);
case ExpressionType.New:
newExpression = (NewExpression)expression;
if (newExpression.Constructor == null)
{
// must be creating a valuetype
Fx.Assert(newExpression.Arguments.Count == 0, "NewExpression with null Constructor but some arguments");
return expression;
}
arguments = null;
tmpArguments = newExpression.Arguments;
Fx.Assert(tmpArguments != null, "NewExpression.Arguments must not be null");
if (tmpArguments.Count > 0)
{
arguments = new List<Expression>(tmpArguments.Count);
for (i = 0; i < tmpArguments.Count; i++)
{
expr1 = Rewrite(tmpArguments[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments.Add(expr1);
}
}
return newExpression.Update(arguments);
case ExpressionType.TypeIs:
TypeBinaryExpression typeBinary = (TypeBinaryExpression)expression;
expr1 = Rewrite(typeBinary.Expression, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.TypeIs(expr1, typeBinary.TypeOperand);
case ExpressionType.ArrayLength:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
UnaryExpression unary = (UnaryExpression)expression;
expr1 = Rewrite(unary.Operand, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.MakeUnary(unary.NodeType, expr1, unary.Type, unary.Method);
case ExpressionType.UnaryPlus:
UnaryExpression unaryPlus = (UnaryExpression)expression;
expr1 = Rewrite(unaryPlus.Operand, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.UnaryPlus(expr1, unaryPlus.Method);
// Expression Tree V2.0 types. This is due to the hosted VB compiler generating ET V2.0 nodes
case ExpressionType.Block:
BlockExpression block = (BlockExpression)expression;
ReadOnlyCollection<ParameterExpression> tmpVariables = block.Variables;
Fx.Assert(tmpVariables != null, "BlockExpression.Variables must not be null");
List<ParameterExpression> parameterList = new List<ParameterExpression>(tmpVariables.Count);
for (i = 0; i < tmpVariables.Count; i++)
{
ParameterExpression param = (ParameterExpression)Rewrite(tmpVariables[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
parameterList.Add(param);
}
tmpExpressions = block.Expressions;
Fx.Assert(tmpExpressions != null, "BlockExpression.Expressions must not be null");
List<Expression> expressionList = new List<Expression>(tmpExpressions.Count);
for (i = 0; i < tmpExpressions.Count; i++)
{
expr1 = Rewrite(tmpExpressions[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
expressionList.Add(expr1);
}
return Expression.Block(parameterList, expressionList);
case ExpressionType.Assign:
BinaryExpression assign = (BinaryExpression)expression;
expr1 = Rewrite(assign.Left, lambdaParameters, out abort);
if (abort)
{
return null;
}
expr2 = Rewrite(assign.Right, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.Assign(expr1, expr2);
}
Fx.Assert("Don't understand expression type " + expression.NodeType);
return expression;
}
MemberBinding Rewrite(MemberBinding binding, ReadOnlyCollection<ParameterExpression> lambdaParameters, out bool abort)
{
int i;
int j;
Expression expr1;
ReadOnlyCollection<Expression> tmpArguments;
abort = false;
switch (binding.BindingType)
{
case MemberBindingType.Assignment:
MemberAssignment assignment = (MemberAssignment)binding;
expr1 = Rewrite(assignment.Expression, lambdaParameters, out abort);
if (abort)
{
return null;
}
return Expression.Bind(assignment.Member, expr1);
case MemberBindingType.ListBinding:
MemberListBinding list = (MemberListBinding)binding;
List<ElementInit> initializers = null;
ReadOnlyCollection<ElementInit> tmpInitializers = list.Initializers;
Fx.Assert(tmpInitializers != null, "MemberListBinding.Initializers must not be null");
if (tmpInitializers.Count > 0)
{
initializers = new List<ElementInit>(tmpInitializers.Count);
for (i = 0; i < tmpInitializers.Count; i++)
{
List<Expression> arguments = null;
tmpArguments = tmpInitializers[i].Arguments;
Fx.Assert(tmpArguments != null, "ElementInit.Arguments must not be null");
if (tmpArguments.Count > 0)
{
arguments = new List<Expression>(tmpArguments.Count);
for (j = 0; j < tmpArguments.Count; j++)
{
expr1 = Rewrite(tmpArguments[j], lambdaParameters, out abort);
if (abort)
{
return null;
}
arguments.Add(expr1);
}
}
initializers.Add(Expression.ElementInit(tmpInitializers[i].AddMethod, arguments));
}
}
return Expression.ListBind(list.Member, initializers);
case MemberBindingType.MemberBinding:
MemberMemberBinding member = (MemberMemberBinding)binding;
ReadOnlyCollection<MemberBinding> tmpBindings = member.Bindings;
Fx.Assert(tmpBindings != null, "MemberMeberBinding.Bindings must not be null");
List<MemberBinding> bindings = new List<MemberBinding>(tmpBindings.Count);
for (i = 0; i < tmpBindings.Count; i++)
{
MemberBinding item = Rewrite(tmpBindings[i], lambdaParameters, out abort);
if (abort)
{
return null;
}
bindings.Add(item);
}
return Expression.MemberBind(member.Member, bindings);
default:
Fx.Assert("MemberBinding type '" + binding.BindingType + "' is not supported.");
return binding;
}
}
static ParameterExpression FindParameter(Expression expression)
{
if (expression == null)
{
return null;
}
switch (expression.NodeType)
{
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Coalesce:
case ExpressionType.Divide:
case ExpressionType.Equal:
case ExpressionType.ExclusiveOr:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LeftShift:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.Modulo:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.NotEqual:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.Power:
case ExpressionType.RightShift:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
BinaryExpression binaryExpression = (BinaryExpression)expression;
return FindParameter(binaryExpression.Left) ?? FindParameter(binaryExpression.Right);
case ExpressionType.Conditional:
ConditionalExpression conditional = (ConditionalExpression)expression;
return FindParameter(conditional.Test) ?? FindParameter(conditional.IfTrue) ?? FindParameter(conditional.IfFalse);
case ExpressionType.Constant:
return null;
case ExpressionType.Invoke:
InvocationExpression invocation = (InvocationExpression)expression;
return FindParameter(invocation.Expression) ?? FindParameter(invocation.Arguments);
case ExpressionType.Lambda:
LambdaExpression lambda = (LambdaExpression)expression;
return FindParameter(lambda.Body);
case ExpressionType.ListInit:
ListInitExpression listInit = (ListInitExpression)expression;
return FindParameter(listInit.NewExpression) ?? FindParameter(listInit.Initializers);
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)expression;
return FindParameter(memberExpression.Expression);
case ExpressionType.MemberInit:
MemberInitExpression memberInit = (MemberInitExpression)expression;
return FindParameter(memberInit.NewExpression) ?? FindParameter(memberInit.Bindings);
case ExpressionType.ArrayIndex:
// ArrayIndex can be a MethodCallExpression or a BinaryExpression
MethodCallExpression arrayIndex = expression as MethodCallExpression;
if (arrayIndex != null)
{
return FindParameter(arrayIndex.Object) ?? FindParameter(arrayIndex.Arguments);
}
BinaryExpression alternateIndex = (BinaryExpression)expression;
return FindParameter(alternateIndex.Left) ?? FindParameter(alternateIndex.Right);
case ExpressionType.Call:
MethodCallExpression methodCall = (MethodCallExpression)expression;
return FindParameter(methodCall.Object) ?? FindParameter(methodCall.Arguments);
case ExpressionType.NewArrayInit:
case ExpressionType.NewArrayBounds:
NewArrayExpression newArray = (NewArrayExpression)expression;
return FindParameter(newArray.Expressions);
case ExpressionType.New:
NewExpression newExpression = (NewExpression)expression;
return FindParameter(newExpression.Arguments);
case ExpressionType.Parameter:
ParameterExpression parameterExpression = (ParameterExpression)expression;
if (parameterExpression.Type == typeof(ActivityContext) && parameterExpression.Name == "context")
{
return parameterExpression;
}
return null;
case ExpressionType.TypeIs:
TypeBinaryExpression typeBinary = (TypeBinaryExpression)expression;
return FindParameter(typeBinary.Expression);
case ExpressionType.ArrayLength:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.Quote:
case ExpressionType.TypeAs:
case ExpressionType.UnaryPlus:
UnaryExpression unary = (UnaryExpression)expression;
return FindParameter(unary.Operand);
// Expression Tree V2.0 types
case ExpressionType.Block:
BlockExpression block = (BlockExpression)expression;
ParameterExpression toReturn = FindParameter(block.Expressions);
if (toReturn != null)
{
return toReturn;
}
List<Expression> variableList = new List<Expression>();
foreach (ParameterExpression variable in block.Variables)
{
variableList.Add(variable);
}
return FindParameter(variableList);
case ExpressionType.Assign:
BinaryExpression assign = (BinaryExpression)expression;
return FindParameter(assign.Left) ?? FindParameter(assign.Right);
}
Fx.Assert("Don't understand expression type " + expression.NodeType);
return null;
}
static ParameterExpression FindParameter(ICollection<Expression> collection)
{
foreach (Expression expression in collection)
{
ParameterExpression result = FindParameter(expression);
if (result != null)
{
return result;
}
}
return null;
}
static ParameterExpression FindParameter(ICollection<ElementInit> collection)
{
foreach (ElementInit init in collection)
{
ParameterExpression result = FindParameter(init.Arguments);
if (result != null)
{
return result;
}
}
return null;
}
static ParameterExpression FindParameter(ICollection<MemberBinding> bindings)
{
foreach (MemberBinding binding in bindings)
{
ParameterExpression result;
switch (binding.BindingType)
{
case MemberBindingType.Assignment:
MemberAssignment assignment = (MemberAssignment)binding;
result = FindParameter(assignment.Expression);
break;
case MemberBindingType.ListBinding:
MemberListBinding list = (MemberListBinding)binding;
result = FindParameter(list.Initializers);
break;
case MemberBindingType.MemberBinding:
MemberMemberBinding member = (MemberMemberBinding)binding;
result = FindParameter(member.Bindings);
break;
default:
Fx.Assert("MemberBinding type '" + binding.BindingType + "' is not supported.");
result = null;
break;
}
if (result != null)
{
return result;
}
}
return null;
}
static void EnsureTypeReferenced(Type type, ref HashSet<Type> typeReferences)
{
// lookup cache
// underlying assumption is that type's inheritance(or interface) hierarchy
// stays static throughout the lifetime of AppDomain
HashSet<Type> alreadyVisited = (HashSet<Type>)typeReferenceCache.GetValue(typeReferenceCacheLock, type);
if (alreadyVisited != null)
{
if (typeReferences == null)
{
// used in VBHelper.Compile<>
// must not alter this set being returned for integrity of cache
typeReferences = alreadyVisited;
}
else
{
// used in VBDesignerHelper.FindTypeReferences
typeReferences.UnionWith(alreadyVisited);
}
return;
}
alreadyVisited = new HashSet<Type>();
EnsureTypeReferencedRecurse(type, alreadyVisited);
// cache resulting alreadyVisited set for fast future lookup
lock (typeReferenceCacheLock)
{
typeReferenceCache.Add(type, alreadyVisited);
}
if (typeReferences == null)
{
// used in VBHelper.Compile<>
// must not alter this set being returned for integrity of cache
typeReferences = alreadyVisited;
}
else
{
// used in VBDesignerHelper.FindTypeReferences
typeReferences.UnionWith(alreadyVisited);
}
return;
}
static void EnsureTypeReferencedRecurse(Type type, HashSet<Type> alreadyVisited)
{
if (alreadyVisited.Contains(type))
{
// this prevents circular reference
// example), class Foo : IBar<Foo>
return;
}
alreadyVisited.Add(type);
// make sure any interfaces needed by this type are referenced
Type[] interfaces = type.GetInterfaces();
for (int i = 0; i < interfaces.Length; ++i)
{
EnsureTypeReferencedRecurse(interfaces[i], alreadyVisited);
}
// same for base types
Type baseType = type.BaseType;
while ((baseType != null) && (baseType != TypeHelper.ObjectType))
{
EnsureTypeReferencedRecurse(baseType, alreadyVisited);
baseType = baseType.BaseType;
}
// for generic types, all type arguments
if (type.IsGenericType)
{
Type[] typeArgs = type.GetGenericArguments();
for (int i = 1; i < typeArgs.Length; ++i)
{
EnsureTypeReferencedRecurse(typeArgs[i], alreadyVisited);
}
}
// array types
if (type.HasElementType)
{
EnsureTypeReferencedRecurse(type.GetElementType(), alreadyVisited);
}
return;
}
static LocationReference FindLocationReferencesFromEnvironment(LocationReferenceEnvironment environment, FindMatch findMatch, string targetName, Type targetType, out bool foundMultiple)
{
LocationReferenceEnvironment currentEnvironment = environment;
foundMultiple = false;
while (currentEnvironment != null)
{
LocationReference toReturn = null;
foreach (LocationReference reference in currentEnvironment.GetLocationReferences())
{
bool terminateSearch;
if (findMatch(reference, targetName, targetType, out terminateSearch))
{
if (toReturn != null)
{
foundMultiple = true;
return toReturn;
}
toReturn = reference;
}
if (terminateSearch)
{
return toReturn;
}
}
if (toReturn != null)
{
return toReturn;
}
currentEnvironment = currentEnvironment.Parent;
}
return null;
}
// this is a place holder for LambdaExpression(raw Expression Tree) that is to be stored in the cache
// this wrapper is necessary because HopperCache requires that once you already have a key along with its associated value in the cache
// you cannot add the same key with a different value.
class RawTreeCacheValueWrapper
{
public LambdaExpression Value { get; set; }
}
class RawTreeCacheKey
{
static IEqualityComparer<HashSet<Assembly>> AssemblySetEqualityComparer = HashSet<Assembly>.CreateSetComparer();
static IEqualityComparer<HashSet<string>> NamespaceSetEqualityComparer = HashSet<string>.CreateSetComparer();
string expressionText;
Type returnType;
HashSet<Assembly> assemblies;
HashSet<string> namespaces;
readonly int hashCode;
public RawTreeCacheKey(string expressionText, Type returnType, HashSet<Assembly> assemblies, HashSet<string> namespaces)
{
this.expressionText = expressionText;
this.returnType = returnType;
this.assemblies = new HashSet<Assembly>(assemblies);
this.namespaces = new HashSet<string>(namespaces);
this.hashCode = expressionText != null ? expressionText.GetHashCode() : 0;
this.hashCode = CombineHashCodes(this.hashCode, AssemblySetEqualityComparer.GetHashCode(this.assemblies));
this.hashCode = CombineHashCodes(this.hashCode, NamespaceSetEqualityComparer.GetHashCode(this.namespaces));
if (this.returnType != null)
{
this.hashCode = CombineHashCodes(this.hashCode, returnType.GetHashCode());
}
}
public override bool Equals(object obj)
{
RawTreeCacheKey rtcKey = obj as RawTreeCacheKey;
if (rtcKey == null || this.hashCode != rtcKey.hashCode)
{
return false;
}
return this.expressionText == rtcKey.expressionText &&
this.returnType == rtcKey.returnType &&
AssemblySetEqualityComparer.Equals(this.assemblies, rtcKey.assemblies) &&
NamespaceSetEqualityComparer.Equals(this.namespaces, rtcKey.namespaces);
}
public override int GetHashCode()
{
return this.hashCode;
}
static int CombineHashCodes(int h1, int h2)
{
return ((h1 << 5) + h1) ^ h2;
}
}
class VisualBasicImportScope : IImportScope
{
IList<Import> importList;
public VisualBasicImportScope(IList<Import> importList)
{
this.importList = importList;
}
public IList<Import> GetImports()
{
return this.importList;
}
}
class VisualBasicScriptAndTypeScope : IScriptScope, ITypeScope
{
LocationReferenceEnvironment environmentProvider;
List<Assembly> assemblies;
string errorMessage;
public VisualBasicScriptAndTypeScope(LocationReferenceEnvironment environmentProvider, List<Assembly> assemblies)
{
this.environmentProvider = environmentProvider;
this.assemblies = assemblies;
}
public string ErrorMessage
{
get { return this.errorMessage; }
}
public Type FindVariable(string name)
{
LocationReference referenceToReturn = null;
FindMatch findMatch = delegateFindAllLocationReferenceMatch;
bool foundMultiple;
referenceToReturn = FindLocationReferencesFromEnvironment(this.environmentProvider, findMatch, name, null, out foundMultiple);
if (referenceToReturn != null)
{
if (foundMultiple)
{
// we have duplicate variable names in the same visible environment!!!!
// compile error here!!!!
this.errorMessage = SR.AmbiguousVBVariableReference(name);
return null;
}
else
{
return referenceToReturn.Type;
}
}
return null;
}
public Type[] FindTypes(string typeName, string nsPrefix)
{
return null;
}
public bool NamespaceExists(string ns)
{
return false;
}
}
[Fx.Tag.SecurityNote(Critical = "Critical because it holds a HostedCompiler instance, which requires FullTrust.")]
[SecurityCritical]
class HostedCompilerWrapper
{
object wrapperLock;
HostedCompiler compiler;
bool isCached;
int refCount;
public HostedCompilerWrapper(HostedCompiler compiler)
{
Fx.Assert(compiler != null, "HostedCompilerWrapper must be assigned a non-null compiler");
this.wrapperLock = new object();
this.compiler = compiler;
this.isCached = true;
this.refCount = 0;
}
public HostedCompiler Compiler
{
get { return this.compiler; }
}
// Storing ticks of the time it last used.
public ulong Timestamp { get; private set; }
// this is called only when this Wrapper is being kicked out the Cache
public void MarkAsKickedOut()
{
HostedCompiler compilerToDispose = null;
lock (this.wrapperLock)
{
this.isCached = false;
if (this.refCount == 0)
{
// if conditions are met,
// Dispose the HostedCompiler
compilerToDispose = this.compiler;
this.compiler = null;
}
}
if (compilerToDispose != null)
{
compilerToDispose.Dispose();
}
}
// this always precedes Compiler.CompileExpression() operation in a thread of execution
// this must never be called after Compiler.Dispose() either in MarkAsKickedOut() or Release()
public void Reserve(ulong timestamp)
{
Fx.Assert(this.isCached, "Can only reserve cached HostedCompiler");
lock (this.wrapperLock)
{
this.refCount++;
}
this.Timestamp = timestamp;
}
// Compiler.CompileExpression() is always followed by this in a thread of execution
public void Release()
{
HostedCompiler compilerToDispose = null;
lock (this.wrapperLock)
{
this.refCount--;
if (!this.isCached && this.refCount == 0)
{
// if conditions are met,
// Dispose the HostedCompiler
compilerToDispose = this.compiler;
this.compiler = null;
}
}
if (compilerToDispose != null)
{
compilerToDispose.Dispose();
}
}
}
}
}
|