//----------------------------------------------------------------------------- // 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 DefaultReferencedAssemblies = new HashSet { 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, 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 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, HostedCompilerWrapper>(HostedCompilerCacheSize, HashSet.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 oldestCompiler = null; foreach (KeyValuePair, 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 referencedAssemblies; HashSet 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 refAssemNames, HashSet namespaceImportsNames) : this(expressionText) { Initialize(refAssemNames, namespaceImportsNames); } VisualBasicHelper(string expressionText) { this.textToCompile = expressionText; } public string TextToCompile { get { return this.textToCompile; } } void Initialize(HashSet refAssemNames, HashSet namespaceImportsNames) { this.namespaceImports = namespaceImportsNames; foreach (AssemblyName assemblyName in refAssemNames) { if (this.referencedAssemblies == null) { this.referencedAssemblies = new HashSet(); } Assembly loaded = AssemblyReference.GetAssembly(assemblyName); if (loaded != null) { this.referencedAssemblies.Add(loaded); } } } public static void GetAllImportReferences(Activity activity, bool isDesignTime, out IList namespaces, out IList assemblies) { List namespaceList = new List(); List assemblyList = new List(); // 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 rootNamespaces; IList 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 namespaces, IList assemblies) { foreach (VisualBasicImportReference importReference in vbSettings.ImportReferences) { namespaces.Add(importReference.Import); assemblies.Add(new AssemblyReference { Assembly = importReference.EarlyBoundAssembly, AssemblyName = importReference.AssemblyName }); } } public static Expression> Compile(string expressionText, CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationExpression) { IList localNamespaces; IList localAssemblies; GetAllImportReferences(publicAccessor.ActivityMetadata.CurrentActivity, false, out localNamespaces, out localAssemblies); VisualBasicHelper helper = new VisualBasicHelper(expressionText); HashSet localReferenceAssemblies = new HashSet(); HashSet localImports = new HashSet(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(); } helper.referencedAssemblies.Add(assemblyReference.Assembly); } else if (assemblyReference.AssemblyName != null) { localReferenceAssemblies.Add(assemblyReference.AssemblyName); } } helper.Initialize(localReferenceAssemblies, localImports); return helper.Compile(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(); } this.referencedAssemblies.UnionWith(DefaultReferencedAssemblies); List importList = new List(); 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()); 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> Compile(CodeActivityPublicEnvironmentAccessor publicAccessor, bool isLocationReference = false) { this.publicAccessor = publicAccessor; return Compile(publicAccessor.ActivityMetadata.Environment, isLocationReference); } // Soft-Link: This method is called through reflection by VisualBasicDesignerHelper. public Expression> Compile(LocationReferenceEnvironment environment) { Fx.Assert(this.publicAccessor == null, "No public accessor so the value for isLocationReference doesn't matter"); return Compile(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> Compile(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(); } this.referencedAssemblies.UnionWith(DefaultReferencedAssemblies); List importList = new List(); 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>(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 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()); 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>(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 lambdaParameters, out bool abort) { return Rewrite(expression, lambdaParameters, false, out abort); } Expression Rewrite(Expression expression, ReadOnlyCollection lambdaParameters, bool isLocationExpression, out bool abort) { int i; int j; Expression expr1; Expression expr2; Expression expr3; List arguments; NewExpression newExpression; ReadOnlyCollection 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(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 tmpInitializers = listInit.Initializers; Fx.Assert(tmpInitializers != null, "ListInitExpression.Initializers must not be null"); List initializers = new List(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(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 tmpMemberBindings = memberInit.Bindings; Fx.Assert(tmpMemberBindings != null, "MemberInitExpression.Bindings must not be null"); List bindings = new List(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 indexes = new List(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(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 tmpExpressions = newArray.Expressions; Fx.Assert(tmpExpressions != null, "NewArrayExpression.Expressions must not be null"); List arrayInitializers = new List(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 bounds = new List(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(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 tmpVariables = block.Variables; Fx.Assert(tmpVariables != null, "BlockExpression.Variables must not be null"); List parameterList = new List(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 expressionList = new List(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 lambdaParameters, out bool abort) { int i; int j; Expression expr1; ReadOnlyCollection 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 initializers = null; ReadOnlyCollection tmpInitializers = list.Initializers; Fx.Assert(tmpInitializers != null, "MemberListBinding.Initializers must not be null"); if (tmpInitializers.Count > 0) { initializers = new List(tmpInitializers.Count); for (i = 0; i < tmpInitializers.Count; i++) { List arguments = null; tmpArguments = tmpInitializers[i].Arguments; Fx.Assert(tmpArguments != null, "ElementInit.Arguments must not be null"); if (tmpArguments.Count > 0) { arguments = new List(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 tmpBindings = member.Bindings; Fx.Assert(tmpBindings != null, "MemberMeberBinding.Bindings must not be null"); List bindings = new List(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 variableList = new List(); 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 collection) { foreach (Expression expression in collection) { ParameterExpression result = FindParameter(expression); if (result != null) { return result; } } return null; } static ParameterExpression FindParameter(ICollection collection) { foreach (ElementInit init in collection) { ParameterExpression result = FindParameter(init.Arguments); if (result != null) { return result; } } return null; } static ParameterExpression FindParameter(ICollection 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 typeReferences) { // lookup cache // underlying assumption is that type's inheritance(or interface) hierarchy // stays static throughout the lifetime of AppDomain HashSet alreadyVisited = (HashSet)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(); 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 alreadyVisited) { if (alreadyVisited.Contains(type)) { // this prevents circular reference // example), class Foo : IBar 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> AssemblySetEqualityComparer = HashSet.CreateSetComparer(); static IEqualityComparer> NamespaceSetEqualityComparer = HashSet.CreateSetComparer(); string expressionText; Type returnType; HashSet assemblies; HashSet namespaces; readonly int hashCode; public RawTreeCacheKey(string expressionText, Type returnType, HashSet assemblies, HashSet namespaces) { this.expressionText = expressionText; this.returnType = returnType; this.assemblies = new HashSet(assemblies); this.namespaces = new HashSet(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 importList; public VisualBasicImportScope(IList importList) { this.importList = importList; } public IList GetImports() { return this.importList; } } class VisualBasicScriptAndTypeScope : IScriptScope, ITypeScope { LocationReferenceEnvironment environmentProvider; List assemblies; string errorMessage; public VisualBasicScriptAndTypeScope(LocationReferenceEnvironment environmentProvider, List 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(); } } } } }