1738 lines
75 KiB
C#
1738 lines
75 KiB
C#
|
//-----------------------------------------------------------------------------
|
|||
|
// 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();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|