//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Activities.XamlIntegration { using System; using System.Text; using System.Activities; using System.Activities.Statements; using System.Activities.Validation; using System.Reflection; using System.CodeDom; using System.CodeDom.Compiler; using System.ComponentModel; using System.Collections.Generic; using Microsoft.VisualBasic.Activities; using Microsoft.VisualBasic; using System.Windows.Markup; using System.Xaml; using System.Activities.Debugger; using System.IO; using System.Activities.Expressions; using System.Runtime; using System.Diagnostics.CodeAnalysis; using System.Security; using System.Security.Permissions; using System.Globalization; using System.Activities.Debugger.Symbol; using System.Linq.Expressions; using System.Diagnostics; public class TextExpressionCompiler { static string typedDataContextName = "_TypedDataContext"; static string expressionGetString = "__Expr{0}Get"; static string expressionSetString = "__Expr{0}Set"; static string expressionStatementString = "__Expr{0}Statement"; static string expressionGetTreeString = "__Expr{0}GetTree"; static string getValueTypeValuesString = "GetValueTypeValues"; static string setValueTypeValuesString = "SetValueTypeValues"; static string valueTypeAccessorString = "ValueType_"; static string forReadOnly = "_ForReadOnly"; static string xamlIntegrationNamespace = "System.Activities.XamlIntegration"; static string rootActivityFieldName = "rootActivity"; static string dataContextActivitiesFieldName = "dataContextActivities"; static string forImplementationName = "forImplementation"; static CodeAttributeDeclaration generatedCodeAttribute; static CodeAttributeDeclaration browsableCodeAttribute; static CodeAttributeDeclaration editorBrowsableCodeAttribute; static string csharpLambdaString = "() => "; static string vbLambdaString = "Function() "; static string locationsOffsetFieldName = "locationsOffset"; static string expectedLocationsCountFieldName = "expectedLocationsCount"; Dictionary> expressionIdToLocationReferences = new Dictionary>(); string activityFullName; int nextContextId; bool? isCS = null; bool? isVB = null; bool generateSource; TextExpressionCompilerSettings settings; List expressionDescriptors; Stack compiledDataContexts; CodeNamespace codeNamespace; CodeTypeDeclaration classDeclaration; CodeCompileUnit compileUnit; Dictionary symbols = null; string fileName = null; // Dictionary of namespace name => [Line#] Dictionary lineNumbersForNSes; Dictionary lineNumbersForNSesForImpl; public TextExpressionCompiler(TextExpressionCompilerSettings settings) { if (settings == null) { throw FxTrace.Exception.ArgumentNull("settings"); } if (settings.Activity == null) { throw FxTrace.Exception.Argument("settings", SR.TextExpressionCompilerActivityRequired); } if (settings.ActivityName == null) { throw FxTrace.Exception.Argument("settings", SR.TextExpressionCompilerActivityNameRequired); } if (settings.Language == null) { throw FxTrace.Exception.Argument("settings", SR.TextExpressionCompilerLanguageRequired); } this.expressionDescriptors = new List(); this.compiledDataContexts = new Stack(); this.nextContextId = 0; this.settings = settings; this.activityFullName = activityFullName = GetActivityFullName(settings); this.generateSource = this.settings.AlwaysGenerateSource; this.lineNumbersForNSes = new Dictionary(); this.lineNumbersForNSesForImpl = new Dictionary(); } bool IsCS { get { if (!isCS.HasValue) { isCS = TextExpression.LanguagesAreEqual(this.settings.Language, "C#"); } return isCS.Value; } } bool IsVB { get { if (!isVB.HasValue) { isVB = TextExpression.LanguagesAreEqual(this.settings.Language, "VB"); } return isVB.Value; } } bool InVariableScopeArgument { get; set; } static CodeAttributeDeclaration GeneratedCodeAttribute { get { if (generatedCodeAttribute == null) { AssemblyName currentAssemblyName = new AssemblyName(Assembly.GetExecutingAssembly().FullName); generatedCodeAttribute = new CodeAttributeDeclaration( new CodeTypeReference(typeof(GeneratedCodeAttribute)), new CodeAttributeArgument(new CodePrimitiveExpression(currentAssemblyName.Name)), new CodeAttributeArgument(new CodePrimitiveExpression(currentAssemblyName.Version.ToString()))); } return generatedCodeAttribute; } } static CodeAttributeDeclaration BrowsableCodeAttribute { get { if (browsableCodeAttribute == null) { browsableCodeAttribute = new CodeAttributeDeclaration( new CodeTypeReference(typeof(BrowsableAttribute)), new CodeAttributeArgument(new CodePrimitiveExpression(false))); } return browsableCodeAttribute; } } static CodeAttributeDeclaration EditorBrowsableCodeAttribute { get { if (editorBrowsableCodeAttribute == null) { editorBrowsableCodeAttribute = new CodeAttributeDeclaration( new CodeTypeReference(typeof(EditorBrowsableAttribute)), new CodeAttributeArgument(new CodeFieldReferenceExpression( new CodeTypeReferenceExpression( new CodeTypeReference(typeof(EditorBrowsableState))), "Never"))); } return editorBrowsableCodeAttribute; } } public bool GenerateSource(TextWriter textWriter) { if (textWriter == null) { throw FxTrace.Exception.ArgumentNull("textWriter"); } Parse(); if (this.generateSource) { WriteCode(textWriter); return true; } return false; } public TextExpressionCompilerResults Compile() { Parse(); if (this.generateSource) { return CompileInMemory(); } return new TextExpressionCompilerResults(); } void Parse() { if (!this.settings.Activity.IsMetadataCached) { IList validationErrors = null; try { ActivityUtilities.CacheRootMetadata(this.settings.Activity, new ActivityLocationReferenceEnvironment(), ProcessActivityTreeOptions.FullCachingOptions, null, ref validationErrors); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CompiledExpressionsCacheMetadataException(this.settings.Activity.GetType().AssemblyQualifiedName, e.ToString()))); } } // Get the source location for an activity if (this.TryGetSymbols(this.settings.Activity, out this.symbols, out this.fileName)) { // Get line number info for namespaces TextExpressionCompilerHelper.GetNamespacesLineInfo(this.fileName, this.lineNumbersForNSes, this.lineNumbersForNSesForImpl); } this.compileUnit = new CodeCompileUnit(); this.codeNamespace = GenerateCodeNamespace(); this.classDeclaration = GenerateClass(); this.codeNamespace.Types.Add(classDeclaration); this.compileUnit.Namespaces.Add(this.codeNamespace); // // Generate data contexts with properties and expression methods // Use the shared, public tree walk for expressions routine for consistency. ExpressionCompilerActivityVisitor visitor = new ExpressionCompilerActivityVisitor(this) { NextExpressionId = 0, }; try { visitor.Visit(this.settings.Activity, this.settings.ForImplementation); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } // // Note that unlike the above where the exception from CacheMetadata is always going to be from the user's code // an exception here is more likely to be from our code and unexpected. However it could be from user code in some cases. // Output a message that attempts to normalize this and presents enough info to the user to determine if they can take action. throw FxTrace.Exception.AsError(new InvalidOperationException(SR.CompiledExpressionsActivityException(e.GetType().FullName, this.settings.Activity.GetType().AssemblyQualifiedName, e.ToString()))); } if (this.generateSource) { GenerateInvokeExpressionMethod(true); GenerateInvokeExpressionMethod(false); GenerateCanExecuteMethod(); GenerateGetRequiredLocationsMethod(); GenerateGetExpressionTreeForExpressionMethod(); } } void OnRootActivity() { // // Always generate a CDC for the root // This will contain expressions for the default value of the root arguments // These expressions cannot see other root arguments or variables so they need // to be at the very root, before we add any properties PushDataContextDescriptor(); } void OnAfterRootActivity() { // // First pop the root arguments descriptor pushed in OnAfterRootArguments PopDataContextDescriptor(); // // If we are walking the implementation there will be a second root context descriptor // that holds the member declarations for root arguments. // This isn't generatedwhen walking the public surface if (this.settings.ForImplementation) { PopDataContextDescriptor(); } } void OnAfterRootArguments(Activity activity) { // // Generate the properties for root arguments in a context below the context // that contains the default expressions for the root arguments CompiledDataContextDescriptor contextDescriptor = PushDataContextDescriptor(); if (activity.RuntimeArguments != null && activity.RuntimeArguments.Count > 0) { // // Walk the arguments foreach (RuntimeArgument runtimeArgument in activity.RuntimeArguments) { if (runtimeArgument.IsBound) { AddMember(runtimeArgument.Name, runtimeArgument.Type, contextDescriptor); } } } } void OnActivityDelegateScope() { PushDataContextDescriptor(); } void OnDelegateArgument(RuntimeDelegateArgument delegateArgument) { AddMember(delegateArgument.BoundArgument.Name, delegateArgument.BoundArgument.Type, this.compiledDataContexts.Peek()); } void OnAfterActivityDelegateScope() { PopDataContextDescriptor(); } void OnVariableScope(Activity activity) { CompiledDataContextDescriptor contextDescriptor = PushDataContextDescriptor(); // // Generate the variable accessors foreach (Variable v in activity.RuntimeVariables) { AddMember(v.Name, v.Type, contextDescriptor); } } void OnRootImplementationScope(Activity activity, out CompiledDataContextDescriptor rootArgumentAccessorContext) { Fx.Assert(this.compiledDataContexts.Count == 2, "The stack of data contexts should contain the root argument default expression and accessor contexts"); rootArgumentAccessorContext = this.compiledDataContexts.Pop(); if (activity.RuntimeVariables != null && activity.RuntimeVariables.Count > 0) { this.OnVariableScope(activity); } } void OnAfterRootImplementationScope(Activity activity, CompiledDataContextDescriptor rootArgumentAccessorContext) { if (activity.RuntimeVariables != null && activity.RuntimeVariables.Count > 0) { OnAfterVariableScope(); } this.compiledDataContexts.Push(rootArgumentAccessorContext); } void AddMember(string name, Type type, CompiledDataContextDescriptor contextDescriptor) { if (IsValidTextIdentifierName(name)) { // // These checks will be invariantlowercase if the language is VB if (contextDescriptor.Fields.ContainsKey(name) || contextDescriptor.Properties.ContainsKey(name)) { if (!contextDescriptor.Duplicates.Contains(name)) { contextDescriptor.Duplicates.Add(name.ToUpperInvariant()); } } else { MemberData memberData = new MemberData(); memberData.Type = type; memberData.Name = name; memberData.Index = contextDescriptor.NextMemberIndex; if (type.IsValueType) { contextDescriptor.Fields.Add(name, memberData); } else { contextDescriptor.Properties.Add(name, memberData); } } } // // Regardless of whether or not this member name is an invalid, duplicate, or valid identifier // always increment the member count so that the indexes we generate always match // the list that the runtime gives to the ITextExpression // The exception here is if the name is null if (name != null) { contextDescriptor.NextMemberIndex++; } } void GenerateMembers(CompiledDataContextDescriptor descriptor) { foreach (KeyValuePair property in descriptor.Properties) { GenerateProperty(property.Value, descriptor); } if (descriptor.Fields.Count > 0) { foreach (KeyValuePair field in descriptor.Fields) { GenerateField(field.Value, descriptor); } CodeMemberMethod getValueTypeValuesMethod = GenerateGetValueTypeValues(descriptor); descriptor.CodeTypeDeclaration.Members.Add(getValueTypeValuesMethod); descriptor.CodeTypeDeclaration.Members.Add(GenerateSetValueTypeValues(descriptor)); descriptor.CodeTypeDeclarationForReadOnly.Members.Add(getValueTypeValuesMethod); } if (descriptor.Duplicates.Count > 0 && this.IsVB) { foreach (string duplicate in descriptor.Duplicates) { AddPropertyForDuplicates(duplicate, descriptor); } } } void GenerateField(MemberData memberData, CompiledDataContextDescriptor contextDescriptor) { if (contextDescriptor.Duplicates.Contains(memberData.Name)) { return; } CodeMemberField accessorField = new CodeMemberField(); accessorField.Attributes = MemberAttributes.Family | MemberAttributes.Final; accessorField.Name = memberData.Name; accessorField.Type = new CodeTypeReference(memberData.Type); if (IsRedefinition(memberData.Name)) { accessorField.Attributes |= MemberAttributes.New; } contextDescriptor.CodeTypeDeclaration.Members.Add(accessorField); contextDescriptor.CodeTypeDeclarationForReadOnly.Members.Add(accessorField); } void GenerateProperty(MemberData memberData, CompiledDataContextDescriptor contextDescriptor) { if (contextDescriptor.Duplicates.Contains(memberData.Name)) { return; } bool isRedefinition = IsRedefinition(memberData.Name); CodeMemberProperty accessorProperty = GenerateCodeMemberProperty(memberData, isRedefinition); // // Generate a get accessor that looks like this: // return (Foo) this.GetVariableValue(contextId, locationIndexId) CodeMethodReturnStatement getterStatement = new CodeMethodReturnStatement( new CodeCastExpression(memberData.Type, new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), "GetVariableValue"), new CodeBinaryOperatorExpression( new CodePrimitiveExpression(memberData.Index), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("locationsOffset"))))); accessorProperty.GetStatements.Add(getterStatement); // Generate a set accessor that looks something like this: // this.SetVariableValue(contextId, locationIndexId, value) accessorProperty.SetStatements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), "SetVariableValue"), new CodeBinaryOperatorExpression( new CodePrimitiveExpression(memberData.Index), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("locationsOffset")), new CodePropertySetValueReferenceExpression())); contextDescriptor.CodeTypeDeclaration.Members.Add(accessorProperty); // // Create another property for the read only class. // This will only have a getter so we can't just re-use the property from above CodeMemberProperty accessorPropertyForReadOnly = GenerateCodeMemberProperty(memberData, isRedefinition); // // OK to share the getter statement from above accessorPropertyForReadOnly.GetStatements.Add(getterStatement); contextDescriptor.CodeTypeDeclarationForReadOnly.Members.Add(accessorPropertyForReadOnly); } CodeMemberProperty GenerateCodeMemberProperty(MemberData memberData, bool isRedefinition) { CodeMemberProperty accessorProperty = new CodeMemberProperty(); accessorProperty.Attributes = MemberAttributes.Family | MemberAttributes.Final; accessorProperty.Name = memberData.Name; accessorProperty.Type = new CodeTypeReference(memberData.Type); if (isRedefinition) { accessorProperty.Attributes |= MemberAttributes.New; } return accessorProperty; } void AddPropertyForDuplicates(string name, CompiledDataContextDescriptor contextDescriptor) { CodeMemberProperty accessorProperty = new CodeMemberProperty(); accessorProperty.Attributes = MemberAttributes.Family | MemberAttributes.Final; accessorProperty.Name = name; accessorProperty.Type = new CodeTypeReference(typeof(object)); CodeThrowExceptionStatement exception = new CodeThrowExceptionStatement( new CodeObjectCreateExpression(typeof(InvalidOperationException), new CodePrimitiveExpression(SR.CompiledExpressionsDuplicateName(name)))); accessorProperty.GetStatements.Add(exception); accessorProperty.SetStatements.Add(exception); contextDescriptor.CodeTypeDeclaration.Members.Add(accessorProperty); // // Create another property for the read only class. // This will only have a getter so we can't just re-use the property from above CodeMemberProperty accessorPropertyForReadOnly = new CodeMemberProperty(); accessorPropertyForReadOnly.Attributes = MemberAttributes.Family | MemberAttributes.Final; accessorPropertyForReadOnly.Name = name; accessorPropertyForReadOnly.Type = new CodeTypeReference(typeof(object)); // // OK to share the exception from above accessorPropertyForReadOnly.GetStatements.Add(exception); contextDescriptor.CodeTypeDeclarationForReadOnly.Members.Add(accessorPropertyForReadOnly); } [Fx.Tag.SecurityNote(Critical = "Critical because we are accessing CodeDom.", Safe = "Safe because we are demanding FullTrust")] [SecuritySafeCritical] [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] bool IsValidTextIdentifierName(string name) { if (string.IsNullOrWhiteSpace(name)) { if (this.settings.LogSourceGenerationMessage != null) { this.settings.LogSourceGenerationMessage(SR.CompiledExpressionsIgnoringUnnamedVariable); } return false; } if (!CodeDomProvider.CreateProvider(this.settings.Language).IsValidIdentifier(name)) { if (this.settings.LogSourceGenerationMessage != null) { this.settings.LogSourceGenerationMessage(SR.CompiledExpressionsIgnoringInvalidIdentifierVariable(name)); } return false; } return true; } bool IsRedefinition(string variableName) { if (this.compiledDataContexts == null) { return false; } foreach (CompiledDataContextDescriptor contextDescriptor in this.compiledDataContexts) { foreach (KeyValuePair field in contextDescriptor.Fields) { if (NamesMatch(variableName, field.Key)) { return true; } } foreach (KeyValuePair property in contextDescriptor.Properties) { if (NamesMatch(variableName, property.Key)) { return true; } } } return false; } bool NamesMatch(string toCheck, string current) { if (IsVB && string.Compare(toCheck, current, true, CultureInfo.CurrentCulture) == 0) { return true; } else if (!IsVB && toCheck == current) { return true; } return false; } void OnAfterVariableScope() { PopDataContextDescriptor(); } void OnITextExpressionFound(Activity activity, ExpressionCompilerActivityVisitor visitor) { CompiledDataContextDescriptor contextDescriptor = null; CompiledDataContextDescriptor currentContextDescriptor = this.compiledDataContexts.Peek(); if (this.InVariableScopeArgument) { // // Temporarily popping the stack so don't use PopDataContextDescriptor // because that is for when the descriptor is done being built this.compiledDataContexts.Pop(); contextDescriptor = PushDataContextDescriptor(); } else { contextDescriptor = currentContextDescriptor; } if (TryGenerateExpressionCode(activity, contextDescriptor, visitor.NextExpressionId, this.settings.Language)) { expressionIdToLocationReferences.Add(visitor.NextExpressionId, this.FindLocationReferences(activity)); visitor.NextExpressionId++; this.generateSource = true; } if (this.InVariableScopeArgument) { PopDataContextDescriptor(); this.compiledDataContexts.Push(currentContextDescriptor); } } IList FindLocationReferences(Activity activity) { ActivityWithResult boundExpression; LocationReference locationReference; List requiredLocationReferences = new List(); foreach (RuntimeArgument runtimeArgument in activity.RuntimeArguments) { boundExpression = runtimeArgument.BoundArgument.Expression; if (boundExpression != null && boundExpression is ILocationReferenceWrapper) { locationReference = ((ILocationReferenceWrapper)boundExpression).LocationReference; if (locationReference != null) { requiredLocationReferences.Add(locationReference.Name); } } } return requiredLocationReferences; } CodeTypeDeclaration GenerateClass() { CodeTypeDeclaration classDeclaration = new CodeTypeDeclaration(this.settings.ActivityName); classDeclaration.BaseTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); classDeclaration.IsPartial = this.settings.GenerateAsPartialClass; CodeMemberField compiledRootField = new CodeMemberField(new CodeTypeReference(typeof(Activity)), rootActivityFieldName); classDeclaration.Members.Add(compiledRootField); CodeMemberMethod languageProperty = new CodeMemberMethod(); languageProperty.Attributes = MemberAttributes.Final | MemberAttributes.Public; languageProperty.Name = "GetLanguage"; languageProperty.ReturnType = new CodeTypeReference(typeof(string)); languageProperty.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(this.settings.Language))); languageProperty.ImplementationTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); languageProperty.CustomAttributes.Add(GeneratedCodeAttribute); languageProperty.CustomAttributes.Add(BrowsableCodeAttribute); languageProperty.CustomAttributes.Add(EditorBrowsableCodeAttribute); classDeclaration.Members.Add(languageProperty); CodeMemberField dataContextActivitiesField = new CodeMemberField(); dataContextActivitiesField.Attributes = MemberAttributes.Private; dataContextActivitiesField.Name = dataContextActivitiesFieldName; dataContextActivitiesField.Type = new CodeTypeReference(typeof(object)); classDeclaration.Members.Add(dataContextActivitiesField); CodeMemberField forImplementationField = new CodeMemberField(); forImplementationField.Attributes = MemberAttributes.Private; forImplementationField.Name = forImplementationName; forImplementationField.Type = new CodeTypeReference(typeof(bool)); forImplementationField.InitExpression = new CodePrimitiveExpression(this.settings.ForImplementation); classDeclaration.Members.Add(forImplementationField); if (!this.settings.GenerateAsPartialClass) { classDeclaration.Members.Add(GenerateCompiledExpressionRootConstructor()); } return classDeclaration; } CodeConstructor GenerateCompiledExpressionRootConstructor() { CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; constructor.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(Activity)), rootActivityFieldName)); CodeBinaryOperatorExpression nullArgumentExpression = new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression(rootActivityFieldName), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)); CodeConditionStatement nullArgumentCondition = new CodeConditionStatement( nullArgumentExpression, new CodeThrowExceptionStatement( new CodeObjectCreateExpression( new CodeTypeReference(typeof(ArgumentNullException)), new CodePrimitiveExpression(rootActivityFieldName)))); constructor.Statements.Add(nullArgumentCondition); constructor.Statements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), new CodeVariableReferenceExpression(rootActivityFieldName))); return constructor; } Dictionary GetCacheIndicies() { Dictionary contexts = new Dictionary(); int currentIndex = 0; foreach (CompiledExpressionDescriptor descriptor in this.expressionDescriptors) { string name = descriptor.TypeName; if (!contexts.ContainsKey(name)) { contexts.Add(name, currentIndex++); } } return contexts; } void GenerateGetRequiredLocationsMethod() { CodeMemberMethod getLocationsMethod = new CodeMemberMethod(); getLocationsMethod.Name = "GetRequiredLocations"; getLocationsMethod.Attributes = MemberAttributes.Final | MemberAttributes.Public; getLocationsMethod.CustomAttributes.Add(GeneratedCodeAttribute); getLocationsMethod.CustomAttributes.Add(BrowsableCodeAttribute); getLocationsMethod.CustomAttributes.Add(EditorBrowsableCodeAttribute); getLocationsMethod.ImplementationTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); getLocationsMethod.ReturnType = new CodeTypeReference(typeof(IList)); getLocationsMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "expressionId")); if (this.IsVB) { GenerateRequiredLocationsBody(getLocationsMethod); } else { GenerateEmptyRequiredLocationsBody(getLocationsMethod); } classDeclaration.Members.Add(getLocationsMethod); } void GenerateEmptyRequiredLocationsBody(CodeMemberMethod getLocationsMethod) { getLocationsMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null))); } void GenerateRequiredLocationsBody(CodeMemberMethod getLocationsMethod) { CodeVariableDeclarationStatement returnLocationsVar = new CodeVariableDeclarationStatement(new CodeTypeReference(typeof(List)), "returnLocations", new CodeObjectCreateExpression(new CodeTypeReference(typeof(List)))); getLocationsMethod.Statements.Add(returnLocationsVar); foreach (CompiledExpressionDescriptor descriptor in expressionDescriptors) { IList requiredLocations = null; bool found = expressionIdToLocationReferences.TryGetValue(descriptor.Id, out requiredLocations); if (!found) { return; } CodeStatement[] conditionStatements = null; conditionStatements = GetRequiredLocationsConditionStatements(requiredLocations); CodeBinaryOperatorExpression idExpression = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("expressionId"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(descriptor.Id)); CodeConditionStatement idCondition = new CodeConditionStatement(idExpression, conditionStatements); getLocationsMethod.Statements.Add(idCondition); } getLocationsMethod.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("returnLocations"))); } static CodeStatement[] GetRequiredLocationsConditionStatements(IList requiredLocations) { CodeStatementCollection statementCollection = new CodeStatementCollection(); foreach (string locationName in requiredLocations) { CodeMethodInvokeExpression invokeValidateExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeVariableReferenceExpression("returnLocations"), "Add"), new CodePrimitiveExpression(locationName)); statementCollection.Add(invokeValidateExpression); } CodeStatement[] returnStatements = new CodeStatement[statementCollection.Count]; statementCollection.CopyTo(returnStatements, 0); return returnStatements; } void GenerateGetExpressionTreeForExpressionMethod() { CodeMemberMethod getExpressionTreeForExpressionMethod = new CodeMemberMethod(); getExpressionTreeForExpressionMethod.Name = "GetExpressionTreeForExpression"; getExpressionTreeForExpressionMethod.Attributes = MemberAttributes.Final | MemberAttributes.Public; getExpressionTreeForExpressionMethod.ReturnType = new CodeTypeReference(typeof(Expression)); getExpressionTreeForExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "expressionId")); getExpressionTreeForExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IList)), "locationReferences")); getExpressionTreeForExpressionMethod.ImplementationTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); // Mark this type as tool generated code getExpressionTreeForExpressionMethod.CustomAttributes.Add(GeneratedCodeAttribute); // Mark it as Browsable(false) // Note that this does not prevent intellisense within a single project, just at the metadata level getExpressionTreeForExpressionMethod.CustomAttributes.Add(BrowsableCodeAttribute); // Mark it as EditorBrowsable(EditorBrowsableState.Never) // Note that this does not prevent intellisense within a single project, just at the metadata level getExpressionTreeForExpressionMethod.CustomAttributes.Add(EditorBrowsableCodeAttribute); foreach (CompiledExpressionDescriptor descriptor in expressionDescriptors) { CodeMethodReturnStatement conditionStatement = new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeObjectCreateExpression(new CodeTypeReference(descriptor.TypeName), new CodeExpression[] { new CodeVariableReferenceExpression("locationReferences") }), descriptor.GetExpressionTreeMethodName))); CodeBinaryOperatorExpression idExpression = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("expressionId"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(descriptor.Id)); CodeConditionStatement idCondition = new CodeConditionStatement(idExpression, conditionStatement); getExpressionTreeForExpressionMethod.Statements.Add(idCondition); } getExpressionTreeForExpressionMethod.Statements.Add(new CodeMethodReturnStatement( new CodePrimitiveExpression(null))); classDeclaration.Members.Add(getExpressionTreeForExpressionMethod); } void GenerateInvokeExpressionMethod(bool withLocationReferences) { CodeMemberMethod invokeExpressionMethod = new CodeMemberMethod(); invokeExpressionMethod.Name = "InvokeExpression"; invokeExpressionMethod.Attributes = MemberAttributes.Final | MemberAttributes.Public; invokeExpressionMethod.CustomAttributes.Add(GeneratedCodeAttribute); invokeExpressionMethod.CustomAttributes.Add(BrowsableCodeAttribute); invokeExpressionMethod.CustomAttributes.Add(EditorBrowsableCodeAttribute); invokeExpressionMethod.ImplementationTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); invokeExpressionMethod.ReturnType = new CodeTypeReference(typeof(object)); invokeExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "expressionId")); if (withLocationReferences) { invokeExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IList)), "locations")); invokeExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(ActivityContext)), "activityContext")); } else { invokeExpressionMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IList)), "locations")); } if (this.settings.GenerateAsPartialClass) { invokeExpressionMethod.Statements.Add(GenerateInitializeDataContextActivity()); } if (withLocationReferences) { if (this.expressionDescriptors != null && this.expressionDescriptors.Count > 0) { // // We only generate the helper method on the root data context/context 0 // No need to have it on all contexts. This is just a slight of hand // so that we don't need to make GetDataContextActivities public on CompiledDataContext. invokeExpressionMethod.Statements.Add(GenerateDataContextActivitiesCheck(this.expressionDescriptors[0])); } } Dictionary cacheIndicies = GetCacheIndicies(); foreach (CompiledExpressionDescriptor descriptor in expressionDescriptors) { // // if ((expressionId == [descriptor.Id])) // { // if (!CheckExpressionText(expressionId, activityContext) // { // throw new Exception(); // } // System.Activities.XamlIntegration.CompiledDataContext[] cachedCompiledDataContext = Workflow1_TypedDataContext1_ForReadOnly.GetCompiledDataContextCacheHelper(this, activityContext, 1); // if ((cachedCompiledDataContext[0] == null)) // { // cachedCompiledDataContext[0] = new Workflow1_TypedDataContext1_ForReadOnly(locations, activityContext); // } // Workflow1_TypedDataContext1_ForReadOnly valDataContext0 = ((Workflow1_TypedDataContext1_ForReadOnly)(cachedCompiledDataContext[0])); // return valDataContext0.ValueType___Expr0Get(); // } // CodeStatement[] conditionStatements = null; if (descriptor.IsReference) { conditionStatements = GenerateReferenceExpressionInvocation(descriptor, withLocationReferences, cacheIndicies); } else if (descriptor.IsValue) { conditionStatements = GenerateValueExpressionInvocation(descriptor, withLocationReferences, cacheIndicies); } else if (descriptor.IsStatement) { conditionStatements = GenerateStatementInvocation(descriptor, withLocationReferences, cacheIndicies); } CodeBinaryOperatorExpression idExpression = new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("expressionId"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(descriptor.Id)); CodeConditionStatement idCondition = new CodeConditionStatement(idExpression, conditionStatements); invokeExpressionMethod.Statements.Add(idCondition); } invokeExpressionMethod.Statements.Add(new CodeMethodReturnStatement( new CodePrimitiveExpression(null))); classDeclaration.Members.Add(invokeExpressionMethod); } CodeConditionStatement GenerateDataContextActivitiesCheck(CompiledExpressionDescriptor descriptor) { CodeBinaryOperatorExpression dataContextActivitiesNullExpression = new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), dataContextActivitiesFieldName), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)); CodeConditionStatement dataContextActivitiesNullStatement = new CodeConditionStatement( dataContextActivitiesNullExpression, new CodeAssignStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), dataContextActivitiesFieldName), new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(new CodeTypeReference(descriptor.TypeName)), "GetDataContextActivitiesHelper"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), forImplementationName)))); return dataContextActivitiesNullStatement; } CodeStatement GenerateInitializeDataContextActivity() { // // if (this.rootActivity == null) // { // this.rootActivity == this; // } CodeBinaryOperatorExpression dataContextActivityExpression = new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)); CodeConditionStatement dataContextActivityCheck = new CodeConditionStatement( dataContextActivityExpression, new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), new CodeThisReferenceExpression())); return dataContextActivityCheck; } void GenerateGetDataContextVariable(CompiledExpressionDescriptor descriptor, CodeVariableDeclarationStatement dataContextVariable, CodeStatementCollection statements, bool withLocationReferences, Dictionary cacheIndicies) { CodeObjectCreateExpression dataContext = GenerateDataContextCreateExpression(descriptor.TypeName, withLocationReferences); if (withLocationReferences) { // // System.Activities.XamlIntegration.CompiledDataContext[] cachedCompiledDataContext = CompiledExpressions_TypedDataContext2.GetCompiledDataContextCacheHelper(this, activityContext, 2); // if ((cachedCompiledDataContext[1] == null)) // { // if (!CompiledExpressions_TypedDataContext2.Validate(locations, activityContext)) // { // return false; // } // cachedCompiledDataContext[1] = new CompiledExpressions_TypedDataContext2(locations, activityContext); // } // CodeVariableDeclarationStatement cachedCompiledDataContextArray = new CodeVariableDeclarationStatement( typeof(CompiledDataContext[]), "cachedCompiledDataContext", new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(descriptor.TypeName), "GetCompiledDataContextCacheHelper"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), dataContextActivitiesFieldName), new CodeVariableReferenceExpression("activityContext"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), forImplementationName), new CodePrimitiveExpression(cacheIndicies.Count))); CodeIndexerExpression compiledDataContextIndexer = new CodeIndexerExpression( new CodeVariableReferenceExpression("cachedCompiledDataContext"), new CodePrimitiveExpression(cacheIndicies[descriptor.TypeName])); // // if (cachedCompiledDataContext[index] == null) // { // cachedCompiledDataContext[index] = new TCDC(locations, activityContext); // } // CodeBinaryOperatorExpression nullCacheItemExpression = new CodeBinaryOperatorExpression( compiledDataContextIndexer, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null)); CodeAssignStatement cacheIndexInitializer = new CodeAssignStatement( compiledDataContextIndexer, dataContext); CodeConditionStatement conditionStatement = new CodeConditionStatement( nullCacheItemExpression, cacheIndexInitializer); // // [compiledDataContextVariable] = cachedCompiledDataContext[index] // dataContextVariable.InitExpression = new CodeCastExpression(descriptor.TypeName, compiledDataContextIndexer); statements.Add(cachedCompiledDataContextArray); statements.Add(conditionStatement); } else { // // [compiledDataContextVariable] = new [compiledDataContextType](locations); // dataContextVariable.InitExpression = dataContext; } } CodeStatement[] GenerateReferenceExpressionInvocation(CompiledExpressionDescriptor descriptor, bool withLocationReferences, Dictionary cacheIndicies) { string indexString = descriptor.Id.ToString(CultureInfo.InvariantCulture); string dataContextVariableName = "refDataContext" + indexString; CodeVariableDeclarationStatement dataContextVariable = new CodeVariableDeclarationStatement( new CodeTypeReference(descriptor.TypeName), dataContextVariableName); CodeStatementCollection compiledDataContextStatements = new CodeStatementCollection(); GenerateGetDataContextVariable(descriptor, dataContextVariable, compiledDataContextStatements, withLocationReferences, cacheIndicies); compiledDataContextStatements.Add(dataContextVariable); CodeExpression getExpression = null; CodeExpression setExpression = null; if (this.IsVB) { getExpression = new CodeDelegateCreateExpression( new CodeTypeReference(descriptor.TypeName), new CodeVariableReferenceExpression(dataContextVariableName), descriptor.GetMethodName); setExpression = new CodeDelegateCreateExpression( new CodeTypeReference(descriptor.TypeName), new CodeVariableReferenceExpression(dataContextVariableName), descriptor.SetMethodName); } else { getExpression = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression(dataContextVariableName), descriptor.GetMethodName); setExpression = new CodeMethodReferenceExpression(new CodeVariableReferenceExpression(dataContextVariableName), descriptor.SetMethodName); } CodeMethodReferenceExpression getLocationMethod = new CodeMethodReferenceExpression( new CodeVariableReferenceExpression(dataContextVariableName), "GetLocation", new CodeTypeReference[] { new CodeTypeReference(descriptor.ResultType) }); CodeExpression[] getLocationParameters = null; if (withLocationReferences) { getLocationParameters = new CodeExpression[] { getExpression, setExpression, new CodeVariableReferenceExpression("expressionId"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), rootActivityFieldName), new CodeVariableReferenceExpression("activityContext") }; } else { getLocationParameters = new CodeExpression[] { getExpression, setExpression }; } CodeMethodInvokeExpression getLocationExpression = new CodeMethodInvokeExpression( getLocationMethod, getLocationParameters); CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(getLocationExpression); compiledDataContextStatements.Add(returnStatement); CodeStatement[] returnStatements = new CodeStatement[compiledDataContextStatements.Count]; compiledDataContextStatements.CopyTo(returnStatements, 0); return returnStatements; } CodeStatement[] GenerateValueExpressionInvocation(CompiledExpressionDescriptor descriptor, bool withLocationReferences, Dictionary cacheIndicies) { CodeStatementCollection compiledDataContextStatements = new CodeStatementCollection(); string indexString = descriptor.Id.ToString(CultureInfo.InvariantCulture); string dataContextVariableName = "valDataContext" + indexString; CodeVariableDeclarationStatement dataContextVariable = new CodeVariableDeclarationStatement( new CodeTypeReference(descriptor.TypeName), dataContextVariableName); GenerateGetDataContextVariable(descriptor, dataContextVariable, compiledDataContextStatements, withLocationReferences, cacheIndicies); compiledDataContextStatements.Add(dataContextVariable); CodeMethodInvokeExpression expressionInvoke = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression(dataContextVariableName), descriptor.GetMethodName)); CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(expressionInvoke); compiledDataContextStatements.Add(returnStatement); CodeStatement[] returnStatements = new CodeStatement[compiledDataContextStatements.Count]; compiledDataContextStatements.CopyTo(returnStatements, 0); return returnStatements; } CodeStatement[] GenerateStatementInvocation(CompiledExpressionDescriptor descriptor, bool withLocationReferences, Dictionary cacheIndicies) { string indexString = descriptor.Id.ToString(CultureInfo.InvariantCulture); string dataContextVariableName = "valDataContext" + indexString; CodeVariableDeclarationStatement dataContextVariable = new CodeVariableDeclarationStatement( new CodeTypeReference(descriptor.TypeName), dataContextVariableName); CodeStatementCollection compiledDataContextStatements = new CodeStatementCollection(); GenerateGetDataContextVariable(descriptor, dataContextVariable, compiledDataContextStatements, withLocationReferences, cacheIndicies); compiledDataContextStatements.Add(dataContextVariable); CodeMethodInvokeExpression expressionInvoke = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeVariableReferenceExpression(dataContextVariableName), descriptor.StatementMethodName)); CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(new CodePrimitiveExpression(null)); compiledDataContextStatements.Add(expressionInvoke); compiledDataContextStatements.Add(returnStatement); CodeStatement[] returnStatements = new CodeStatement[compiledDataContextStatements.Count]; compiledDataContextStatements.CopyTo(returnStatements, 0); return returnStatements; } void GenerateCanExecuteMethod() { CodeMemberMethod isValidMethod = new CodeMemberMethod(); isValidMethod.Name = "CanExecuteExpression"; isValidMethod.ReturnType = new CodeTypeReference(typeof(bool)); isValidMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; isValidMethod.CustomAttributes.Add(GeneratedCodeAttribute); isValidMethod.CustomAttributes.Add(BrowsableCodeAttribute); isValidMethod.CustomAttributes.Add(EditorBrowsableCodeAttribute); isValidMethod.ImplementationTypes.Add(new CodeTypeReference(typeof(ICompiledExpressionRoot))); isValidMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(string)), "expressionText")); isValidMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(bool)), "isReference")); isValidMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(IList)), "locations")); CodeParameterDeclarationExpression expressionIdParam = new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "expressionId"); expressionIdParam.Direction = FieldDirection.Out; isValidMethod.Parameters.Add(expressionIdParam); // // if (((isReference == false) // && ((expressionText == [expression text]) // && ([data context type name].Validate(locations, true) == true)))) // { // expressionId = [id for expression text and data context]; // return true; // } // foreach (CompiledExpressionDescriptor descriptor in expressionDescriptors) { CodeBinaryOperatorExpression checkIsReferenceExpression = new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression("isReference"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(descriptor.IsReference)); CodeBinaryOperatorExpression checkTextExpression = new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression("expressionText"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(descriptor.ExpressionText)); CodeMethodInvokeExpression invokeValidateExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(descriptor.TypeName), "Validate"), new CodeVariableReferenceExpression("locations"), new CodePrimitiveExpression(true), new CodePrimitiveExpression(0)); CodeBinaryOperatorExpression checkValidateExpression = new CodeBinaryOperatorExpression( invokeValidateExpression, CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true)); CodeBinaryOperatorExpression checkTextAndValidateExpression = new CodeBinaryOperatorExpression( checkTextExpression, CodeBinaryOperatorType.BooleanAnd, checkValidateExpression); CodeBinaryOperatorExpression checkIsReferenceAndTextAndValidateExpression = new CodeBinaryOperatorExpression( checkIsReferenceExpression, CodeBinaryOperatorType.BooleanAnd, checkTextAndValidateExpression); CodeAssignStatement assignId = new CodeAssignStatement( new CodeVariableReferenceExpression("expressionId"), new CodePrimitiveExpression(descriptor.Id)); CodeConditionStatement matchCondition = new CodeConditionStatement( checkIsReferenceAndTextAndValidateExpression); matchCondition.TrueStatements.Add(assignId); matchCondition.TrueStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(true))); isValidMethod.Statements.Add(matchCondition); } isValidMethod.Statements.Add( new CodeAssignStatement( new CodeVariableReferenceExpression("expressionId"), new CodePrimitiveExpression(-1))); isValidMethod.Statements.Add( new CodeMethodReturnStatement( new CodePrimitiveExpression(false))); classDeclaration.Members.Add(isValidMethod); } CodeObjectCreateExpression GenerateDataContextCreateExpression(string typeName, bool withLocationReferences) { if (withLocationReferences) { return new CodeObjectCreateExpression( new CodeTypeReference(typeName), new CodeVariableReferenceExpression("locations"), new CodeVariableReferenceExpression("activityContext"), new CodePrimitiveExpression(true)); } else { return new CodeObjectCreateExpression( new CodeTypeReference(typeName), new CodeExpression[] { new CodeVariableReferenceExpression("locations"), new CodePrimitiveExpression(true) }); } } bool TryGenerateExpressionCode(Activity activity, CompiledDataContextDescriptor dataContextDescriptor, int nextExpressionId, string language) { ITextExpression textExpression = (ITextExpression)activity; if (!TextExpression.LanguagesAreEqual(textExpression.Language, language) || string.IsNullOrWhiteSpace(textExpression.ExpressionText)) { // // We can only compile expressions that match the project's flavor // and expression activities with no expressions don't need anything generated. return false; } Type resultType = (activity is ActivityWithResult) ? ((ActivityWithResult)activity).ResultType : null; string expressionText = textExpression.ExpressionText; bool isReference = false; bool isValue = false; bool isStatement = false; if (resultType == null) { isStatement = true; } else { isReference = TypeHelper.AreTypesCompatible(resultType, typeof(Location)); isValue = !isReference; } CodeTypeDeclaration typeDeclaration; if (isValue) { typeDeclaration = dataContextDescriptor.CodeTypeDeclarationForReadOnly; } else { // // Statement and reference get read/write context typeDeclaration = dataContextDescriptor.CodeTypeDeclaration; } CompiledExpressionDescriptor descriptor = new CompiledExpressionDescriptor(); descriptor.TypeName = typeDeclaration.Name; descriptor.Id = nextExpressionId; descriptor.ExpressionText = textExpression.ExpressionText; if (isReference) { if (resultType.IsGenericType) { resultType = resultType.GetGenericArguments()[0]; } else { resultType = typeof(object); } } descriptor.ResultType = resultType; GenerateExpressionGetTreeMethod(activity, descriptor, dataContextDescriptor, isValue, isStatement, nextExpressionId); if (isValue || isReference) { CodeMemberMethod expressionGetMethod = GenerateGetMethod(activity, resultType, expressionText, nextExpressionId); typeDeclaration.Members.Add(expressionGetMethod); CodeMemberMethod expressionGetValueTypeAccessorMethod = GenerateGetMethodWrapper(expressionGetMethod); typeDeclaration.Members.Add(expressionGetValueTypeAccessorMethod); descriptor.GetMethodName = expressionGetValueTypeAccessorMethod.Name; } if (isReference) { CodeMemberMethod expressionSetMethod = GenerateSetMethod(activity, resultType, expressionText, nextExpressionId); dataContextDescriptor.CodeTypeDeclaration.Members.Add(expressionSetMethod); CodeMemberMethod expressionSetValueTypeAccessorMethod = GenerateSetMethodWrapper(expressionSetMethod); dataContextDescriptor.CodeTypeDeclaration.Members.Add(expressionSetValueTypeAccessorMethod); descriptor.SetMethodName = expressionSetValueTypeAccessorMethod.Name; } if (isStatement) { CodeMemberMethod statementMethod = GenerateStatementMethod(activity, expressionText, nextExpressionId); dataContextDescriptor.CodeTypeDeclaration.Members.Add(statementMethod); CodeMemberMethod expressionSetValueTypeAccessorMethod = GenerateStatementMethodWrapper(statementMethod); dataContextDescriptor.CodeTypeDeclaration.Members.Add(expressionSetValueTypeAccessorMethod); descriptor.StatementMethodName = expressionSetValueTypeAccessorMethod.Name; } expressionDescriptors.Add(descriptor); return true; } void GenerateExpressionGetTreeMethod(Activity activity, CompiledExpressionDescriptor expressionDescriptor, CompiledDataContextDescriptor dataContextDescriptor, bool isValue, bool isStatement, int nextExpressionId) { CodeMemberMethod expressionMethod = new CodeMemberMethod(); expressionMethod.Attributes = MemberAttributes.Assembly | MemberAttributes.Final; expressionMethod.Name = string.Format(CultureInfo.InvariantCulture, expressionGetTreeString, nextExpressionId); expressionMethod.ReturnType = new CodeTypeReference(typeof(Expression)); expressionDescriptor.GetExpressionTreeMethodName = expressionMethod.Name; if (isStatement) { // Can't generate expression tree for a statement expressionMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null))); dataContextDescriptor.CodeTypeDeclaration.Members.Add(expressionMethod); return; } string coreExpressionText = expressionDescriptor.ExpressionText; CodeLinePragma pragma; AlignText(activity, ref coreExpressionText, out pragma); Type returnType = typeof(Expression<>).MakeGenericType(typeof(Func<>).MakeGenericType(expressionDescriptor.ResultType)); string expressionText = null; if (IsVB) { expressionText = string.Concat(vbLambdaString, coreExpressionText); } else if (IsCS) { expressionText = string.Concat(csharpLambdaString, coreExpressionText); } if (expressionText != null) { CodeVariableDeclarationStatement statement = new CodeVariableDeclarationStatement(returnType, "expression", new CodeSnippetExpression(expressionText)); statement.LinePragma = pragma; expressionMethod.Statements.Add(statement); CodeMethodInvokeExpression invokeExpression = new CodeMethodInvokeExpression( new CodeBaseReferenceExpression(), "RewriteExpressionTree", new CodeExpression[] { new CodeVariableReferenceExpression("expression") }); expressionMethod.Statements.Add(new CodeMethodReturnStatement(invokeExpression)); } else { expressionMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(null))); } if (isValue) { dataContextDescriptor.CodeTypeDeclarationForReadOnly.Members.Add(expressionMethod); } else { dataContextDescriptor.CodeTypeDeclaration.Members.Add(expressionMethod); } } CodeMemberMethod GenerateGetMethod(Activity activity, Type resultType, string expressionText, int nextExpressionId) { CodeMemberMethod expressionMethod = new CodeMemberMethod(); expressionMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; expressionMethod.Name = string.Format(CultureInfo.InvariantCulture, expressionGetString, nextExpressionId); expressionMethod.ReturnType = new CodeTypeReference(resultType); expressionMethod.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(DebuggerHiddenAttribute)))); CodeLinePragma pragma; AlignText(activity, ref expressionText, out pragma); CodeStatement statement = new CodeMethodReturnStatement(new CodeSnippetExpression(expressionText)); statement.LinePragma = pragma; expressionMethod.Statements.Add(statement); return expressionMethod; } CodeMemberMethod GenerateGetMethodWrapper(CodeMemberMethod expressionMethod) { CodeMemberMethod wrapperMethod = new CodeMemberMethod(); wrapperMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; wrapperMethod.Name = valueTypeAccessorString + expressionMethod.Name; wrapperMethod.ReturnType = expressionMethod.ReturnType; wrapperMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), getValueTypeValuesString))); wrapperMethod.Statements.Add(new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), expressionMethod.Name)))); return wrapperMethod; } CodeMemberMethod GenerateSetMethod(Activity activity, Type resultType, string expressionText, int nextExpressionId) { string paramName = "value"; if (string.Compare(expressionText, paramName, true, Globalization.CultureInfo.CurrentCulture) == 0) { paramName += "1"; } CodeMemberMethod expressionMethod = new CodeMemberMethod(); expressionMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; expressionMethod.Name = string.Format(CultureInfo.InvariantCulture, expressionSetString, nextExpressionId); expressionMethod.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(DebuggerHiddenAttribute)))); var exprValueParam = new CodeParameterDeclarationExpression(resultType, paramName); expressionMethod.Parameters.Add(exprValueParam); CodeLinePragma pragma; AlignText(activity, ref expressionText, out pragma); CodeAssignStatement statement = new CodeAssignStatement(new CodeSnippetExpression(expressionText), new CodeArgumentReferenceExpression(paramName)); statement.LinePragma = pragma; expressionMethod.Statements.Add(statement); return expressionMethod; } CodeMemberMethod GenerateSetMethodWrapper(CodeMemberMethod expressionMethod) { CodeMemberMethod wrapperMethod = new CodeMemberMethod(); wrapperMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; wrapperMethod.Name = valueTypeAccessorString + expressionMethod.Name; CodeParameterDeclarationExpression exprValueParam = new CodeParameterDeclarationExpression(expressionMethod.Parameters[0].Type, expressionMethod.Parameters[0].Name); wrapperMethod.Parameters.Add(exprValueParam); wrapperMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), getValueTypeValuesString))); CodeMethodInvokeExpression setExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), expressionMethod.Name)); setExpression.Parameters.Add(new CodeVariableReferenceExpression(expressionMethod.Parameters[0].Name)); wrapperMethod.Statements.Add(setExpression); wrapperMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), setValueTypeValuesString))); return wrapperMethod; } CodeMemberMethod GenerateStatementMethod(Activity activity, string expressionText, int nextExpressionId) { CodeMemberMethod expressionMethod = new CodeMemberMethod(); expressionMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; expressionMethod.Name = string.Format(CultureInfo.InvariantCulture, expressionStatementString, nextExpressionId); expressionMethod.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(DebuggerHiddenAttribute)))); CodeLinePragma pragma; AlignText(activity, ref expressionText, out pragma); CodeStatement statement = new CodeSnippetStatement(expressionText); statement.LinePragma = pragma; expressionMethod.Statements.Add(statement); return expressionMethod; } CodeMemberMethod GenerateStatementMethodWrapper(CodeMemberMethod expressionMethod) { CodeMemberMethod wrapperMethod = new CodeMemberMethod(); wrapperMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final; wrapperMethod.Name = valueTypeAccessorString + expressionMethod.Name; wrapperMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), getValueTypeValuesString))); CodeMethodInvokeExpression setExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), expressionMethod.Name)); wrapperMethod.Statements.Add(setExpression); wrapperMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), setValueTypeValuesString))); return wrapperMethod; } CodeMemberMethod GenerateGetValueTypeValues(CompiledDataContextDescriptor descriptor) { CodeMemberMethod fetchMethod = new CodeMemberMethod(); fetchMethod.Name = getValueTypeValuesString; fetchMethod.Attributes = MemberAttributes.Override | MemberAttributes.Family; foreach (KeyValuePair valueField in descriptor.Fields) { if (descriptor.Duplicates.Contains(valueField.Key)) { continue; } CodeExpression getValue = new CodeCastExpression( valueField.Value.Type, new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), "GetVariableValue"), new CodeBinaryOperatorExpression( new CodePrimitiveExpression(valueField.Value.Index), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("locationsOffset")))); CodeFieldReferenceExpression fieldReference = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), valueField.Key); fetchMethod.Statements.Add( new CodeAssignStatement(fieldReference, getValue)); } fetchMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeBaseReferenceExpression(), fetchMethod.Name))); return fetchMethod; } CodeMemberMethod GenerateSetValueTypeValues(CompiledDataContextDescriptor descriptor) { CodeMemberMethod pushMethod = new CodeMemberMethod(); pushMethod.Name = setValueTypeValuesString; pushMethod.Attributes = MemberAttributes.Override | MemberAttributes.Family; foreach (KeyValuePair valueField in descriptor.Fields) { if (descriptor.Duplicates.Contains(valueField.Key)) { continue; } CodeMethodInvokeExpression setValue = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeThisReferenceExpression(), "SetVariableValue"), new CodeBinaryOperatorExpression( new CodePrimitiveExpression(valueField.Value.Index), CodeBinaryOperatorType.Add, new CodeVariableReferenceExpression("locationsOffset")), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), valueField.Key)); pushMethod.Statements.Add(setValue); } pushMethod.Statements.Add(new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeBaseReferenceExpression(), pushMethod.Name))); return pushMethod; } CodeTypeDeclaration GenerateCompiledDataContext(bool forReadOnly) { string forReadOnlyString = forReadOnly ? TextExpressionCompiler.forReadOnly : string.Empty; string contextName = string.Concat(this.settings.ActivityName, TextExpressionCompiler.typedDataContextName, this.nextContextId, forReadOnlyString); CodeTypeDeclaration typedDataContext = new CodeTypeDeclaration(contextName); typedDataContext.TypeAttributes = TypeAttributes.NestedPrivate; // // data context classes are declared inside of the main class via the partial class to reduce visibility/surface area. this.classDeclaration.Members.Add(typedDataContext); if (this.compiledDataContexts != null && this.compiledDataContexts.Count > 0) { string baseTypeName = null; if (forReadOnly) { baseTypeName = this.compiledDataContexts.Peek().CodeTypeDeclarationForReadOnly.Name; } else { baseTypeName = this.compiledDataContexts.Peek().CodeTypeDeclaration.Name; } typedDataContext.BaseTypes.Add(baseTypeName); } else { typedDataContext.BaseTypes.Add(typeof(CompiledDataContext)); // // We only generate the helper method on the root data context/context 0 // No need to have it on all contexts. This is just a slight of hand // so that we don't need to make GetDataContextActivities public on CompiledDataContext. typedDataContext.Members.Add(GenerateDataContextActivitiesHelper()); } CodeMemberField offsetField = new CodeMemberField(); offsetField.Attributes = MemberAttributes.Private; offsetField.Name = locationsOffsetFieldName; offsetField.Type = new CodeTypeReference(typeof(int)); typedDataContext.Members.Add(offsetField); CodeMemberField expectedLocationsCountField = new CodeMemberField(); expectedLocationsCountField.Attributes = MemberAttributes.Private | MemberAttributes.Static; expectedLocationsCountField.Name = expectedLocationsCountFieldName; expectedLocationsCountField.Type = new CodeTypeReference(typeof(int)); typedDataContext.Members.Add(expectedLocationsCountField); typedDataContext.Members.Add(GenerateLocationReferenceActivityContextConstructor()); typedDataContext.Members.Add(GenerateLocationConstructor()); typedDataContext.Members.Add(GenerateLocationReferenceConstructor()); typedDataContext.Members.Add(GenerateCacheHelper()); typedDataContext.Members.Add(GenerateSetLocationsOffsetMethod()); // // Mark this type as tool generated code typedDataContext.CustomAttributes.Add(GeneratedCodeAttribute); // // Mark it as Browsable(false) // Note that this does not prevent intellisense within a single project, just at the metadata level typedDataContext.CustomAttributes.Add(BrowsableCodeAttribute); // // Mark it as EditorBrowsable(EditorBrowsableState.Never) // Note that this does not prevent intellisense within a single project, just at the metadata level typedDataContext.CustomAttributes.Add(EditorBrowsableCodeAttribute); return typedDataContext; } CodeMemberMethod GenerateDataContextActivitiesHelper() { CodeMemberMethod dataContextActivitiesHelper = new CodeMemberMethod(); dataContextActivitiesHelper.Name = "GetDataContextActivitiesHelper"; dataContextActivitiesHelper.Attributes = MemberAttributes.Assembly | MemberAttributes.Final | MemberAttributes.Static; if (this.compiledDataContexts != null && this.compiledDataContexts.Count > 0) { dataContextActivitiesHelper.Attributes |= MemberAttributes.New; } dataContextActivitiesHelper.ReturnType = new CodeTypeReference(typeof(object)); dataContextActivitiesHelper.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(Activity)), "compiledRoot")); dataContextActivitiesHelper.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(bool)), forImplementationName)); dataContextActivitiesHelper.Statements.Add( new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(typeof(CompiledDataContext)), "GetDataContextActivities"), new CodeVariableReferenceExpression("compiledRoot"), new CodeVariableReferenceExpression(forImplementationName)))); return dataContextActivitiesHelper; } CodeMemberMethod GenerateSetLocationsOffsetMethod() { CodeMemberMethod setLocationsOffsetMethod = new CodeMemberMethod(); setLocationsOffsetMethod.Name = "SetLocationsOffset"; setLocationsOffsetMethod.Parameters.Add(new CodeParameterDeclarationExpression(new CodeTypeReference(typeof(int)), "locationsOffsetValue")); setLocationsOffsetMethod.Attributes = MemberAttributes.Public; if (this.compiledDataContexts.Count > 0) { setLocationsOffsetMethod.Attributes |= MemberAttributes.New; } CodeAssignStatement assignLocationsOffsetStatement = new CodeAssignStatement( new CodeVariableReferenceExpression("locationsOffset"), new CodeVariableReferenceExpression("locationsOffsetValue")); setLocationsOffsetMethod.Statements.Add(assignLocationsOffsetStatement); if (this.nextContextId > 0) { CodeMethodInvokeExpression baseSetLocationsOffsetMethod = new CodeMethodInvokeExpression( new CodeBaseReferenceExpression(), "SetLocationsOffset", new CodeVariableReferenceExpression("locationsOffset")); setLocationsOffsetMethod.Statements.Add(baseSetLocationsOffsetMethod); } return setLocationsOffsetMethod; } CodeMemberMethod GenerateCacheHelper() { CodeMemberMethod cacheHelper = new CodeMemberMethod(); cacheHelper.Name = "GetCompiledDataContextCacheHelper"; cacheHelper.Attributes = MemberAttributes.Assembly | MemberAttributes.Final | MemberAttributes.Static; if (this.compiledDataContexts != null && this.compiledDataContexts.Count > 0) { cacheHelper.Attributes |= MemberAttributes.New; } cacheHelper.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), dataContextActivitiesFieldName)); cacheHelper.Parameters.Add(new CodeParameterDeclarationExpression(typeof(ActivityContext), "activityContext")); cacheHelper.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Activity), "compiledRoot")); cacheHelper.Parameters.Add(new CodeParameterDeclarationExpression(typeof(bool), forImplementationName)); cacheHelper.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "compiledDataContextCount")); cacheHelper.ReturnType = new CodeTypeReference(typeof(CompiledDataContext[])); cacheHelper.Statements.Add( new CodeMethodReturnStatement( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(typeof(CompiledDataContext)), "GetCompiledDataContextCache"), new CodeVariableReferenceExpression(dataContextActivitiesFieldName), new CodeVariableReferenceExpression("activityContext"), new CodeVariableReferenceExpression("compiledRoot"), new CodeVariableReferenceExpression(forImplementationName), new CodeVariableReferenceExpression("compiledDataContextCount")))); return cacheHelper; } CodeConstructor GenerateLocationReferenceActivityContextConstructor() { // // public [typename](IList locations, ActivityContext activityContext) // : base(locations, activityContext) // CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; CodeParameterDeclarationExpression constructorLocationsParam = new CodeParameterDeclarationExpression(typeof(IList), "locations"); constructor.Parameters.Add(constructorLocationsParam); constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("locations")); CodeParameterDeclarationExpression constructorActivityContextParam = new CodeParameterDeclarationExpression(typeof(ActivityContext), "activityContext"); constructor.Parameters.Add(constructorActivityContextParam); constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("activityContext")); CodeParameterDeclarationExpression computelocationsOffsetParam = new CodeParameterDeclarationExpression(typeof(bool), "computelocationsOffset"); constructor.Parameters.Add(computelocationsOffsetParam); if (this.nextContextId > 0) { constructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(false)); } InvokeSetLocationsOffsetMethod(constructor); return constructor; } CodeConstructor GenerateLocationConstructor() { // // public [typename](IList locations, ActivityContext activityContext) // : base(locations) // CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; CodeParameterDeclarationExpression constructorLocationsParam = new CodeParameterDeclarationExpression(typeof(IList), "locations"); constructor.Parameters.Add(constructorLocationsParam); constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("locations")); CodeParameterDeclarationExpression computelocationsOffsetParam = new CodeParameterDeclarationExpression(typeof(bool), "computelocationsOffset"); constructor.Parameters.Add(computelocationsOffsetParam); if (this.nextContextId > 0) { constructor.BaseConstructorArgs.Add(new CodePrimitiveExpression(false)); } InvokeSetLocationsOffsetMethod(constructor); return constructor; } CodeConstructor GenerateLocationReferenceConstructor() { // // public [typename](IList locationReferences) // : base(locationReferences) // CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public; CodeParameterDeclarationExpression constructorLocationsParam = new CodeParameterDeclarationExpression(typeof(IList), "locationReferences"); constructor.Parameters.Add(constructorLocationsParam); constructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("locationReferences")); return constructor; } void InvokeSetLocationsOffsetMethod(CodeConstructor constructor) { CodeExpressionStatement setLocationsOffsetMethod = new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "SetLocationsOffset", new CodeBinaryOperatorExpression( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("locations"), "Count"), CodeBinaryOperatorType.Subtract, new CodeVariableReferenceExpression("expectedLocationsCount")))); CodeConditionStatement offsetCheckStatement = new CodeConditionStatement(new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression("computelocationsOffset"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true)), new CodeStatement[] { setLocationsOffsetMethod }); constructor.Statements.Add(offsetCheckStatement); } CodeNamespace GenerateCodeNamespace() { CodeNamespace codeNamespace = new CodeNamespace(this.settings.ActivityNamespace); bool seenXamlIntegration = false; foreach (string nsReference in GetNamespaceReferences()) { if (!seenXamlIntegration && nsReference == xamlIntegrationNamespace) { seenXamlIntegration = true; } codeNamespace.Imports.Add(new CodeNamespaceImport(nsReference) { LinePragma = GenerateLinePragmaForNamespace(nsReference), }); } if (!seenXamlIntegration) { codeNamespace.Imports.Add(new CodeNamespaceImport(xamlIntegrationNamespace) { LinePragma = GenerateLinePragmaForNamespace(xamlIntegrationNamespace), }); } return codeNamespace; } bool AssemblyContainsTypeWithActivityNamespace() { // We need to include the ActivityNamespace in the imports if there are any types in // the Activity's assembly that are contained in that namespace. Type[] types; try { types = this.settings.Activity.GetType().Assembly.GetTypes(); } catch (ReflectionTypeLoadException) { // We had a problem loading all the types. Take the safe route and assume we need to include the ActivityNamespace. return true; } if (types != null) { foreach (Type type in types) { if (type.Namespace == this.settings.ActivityNamespace) { return true; } } } return false; } IEnumerable GetNamespaceReferences() { HashSet nsReferences = new HashSet(); // Add some namespace imports, use the same base set for C# as for VB, they aren't lang specific foreach (string nsReference in TextExpression.DefaultNamespaces) { nsReferences.Add(nsReference); } VisualBasicSettings vbSettings = null; if (IsVB) { vbSettings = VisualBasic.GetSettings(this.settings.Activity); } if (vbSettings != null) { foreach (VisualBasicImportReference nsReference in vbSettings.ImportReferences) { if (!string.IsNullOrWhiteSpace(nsReference.Import)) { // For VB, the ActivityNamespace has the RootNamespace stripped off. We don't need an Imports reference // to ActivityNamespace, if this reference is in the same assembly and there is a RootNamespace specified. // We check both Assembly.FullName and // Assembly.GetName().Name because testing has shown that nsReference.Assembly sometimes gives fully qualified // names and sometimes not. if ( (nsReference.Import == this.settings.ActivityNamespace) && ((nsReference.Assembly == this.settings.Activity.GetType().Assembly.FullName) || (nsReference.Assembly == this.settings.Activity.GetType().Assembly.GetName().Name)) && !string.IsNullOrWhiteSpace(this.settings.RootNamespace) && !AssemblyContainsTypeWithActivityNamespace() ) { continue; } nsReferences.Add(nsReference.Import); } } } else { IList references = this.settings.ForImplementation ? TextExpression.GetNamespacesForImplementation(this.settings.Activity) : TextExpression.GetNamespaces(this.settings.Activity); foreach (string nsReference in references) { if (!string.IsNullOrWhiteSpace(nsReference)) { nsReferences.Add(nsReference); } } } return nsReferences; } CompiledDataContextDescriptor PushDataContextDescriptor() { CompiledDataContextDescriptor contextDescriptor = new CompiledDataContextDescriptor(() => this.IsVB) { CodeTypeDeclaration = GenerateCompiledDataContext(false), CodeTypeDeclarationForReadOnly = GenerateCompiledDataContext(true), NextMemberIndex = GetStartMemberIndex() }; this.compiledDataContexts.Push(contextDescriptor); this.nextContextId++; return contextDescriptor; } void PopDataContextDescriptor() { CompiledDataContextDescriptor descriptor = this.compiledDataContexts.Pop(); if (descriptor != null) { GenerateMembers(descriptor); GenerateValidate(descriptor, true); GenerateValidate(descriptor, false); } } int GetStartMemberIndex() { if (this.compiledDataContexts == null || this.compiledDataContexts.Count == 0) { return 0; } else { return this.compiledDataContexts.Peek().NextMemberIndex; } } void GenerateValidate(CompiledDataContextDescriptor descriptor, bool forReadOnly) { // // // Validate the locations at runtime match the set at compile time // // protected override bool Validate(IList locationReferences) // { // if (validateLocationCount && locationReferences.Count != [generated count of location references]) // { // return false; // } // if (locationReferences[0].Name != [generated name for index] || // locationReferences[0].Type != typeof([generated type for index])) // { // return false; // } // // ... // // } CodeMemberMethod validateMethod = new CodeMemberMethod(); validateMethod.Name = "Validate"; validateMethod.Attributes = MemberAttributes.Public | MemberAttributes.Static; if (this.compiledDataContexts.Count > 0) { validateMethod.Attributes |= MemberAttributes.New; } validateMethod.ReturnType = new CodeTypeReference(typeof(bool)); validateMethod.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(IList)), "locationReferences")); validateMethod.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(bool)), "validateLocationCount")); validateMethod.Parameters.Add( new CodeParameterDeclarationExpression( new CodeTypeReference(typeof(int)), "offset") ); CodeBinaryOperatorExpression shouldCheckLocationCountExpression = new CodeBinaryOperatorExpression( new CodeVariableReferenceExpression("validateLocationCount"), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(true)); CodeBinaryOperatorExpression compareLocationCountExpression = new CodeBinaryOperatorExpression( new CodePropertyReferenceExpression( new CodeVariableReferenceExpression("locationReferences"), "Count"), CodeBinaryOperatorType.LessThan, new CodePrimitiveExpression(descriptor.NextMemberIndex) ); CodeBinaryOperatorExpression checkLocationCountExpression = new CodeBinaryOperatorExpression( shouldCheckLocationCountExpression, CodeBinaryOperatorType.BooleanAnd, compareLocationCountExpression); CodeConditionStatement checkLocationCountStatement = new CodeConditionStatement( checkLocationCountExpression, new CodeMethodReturnStatement( new CodePrimitiveExpression(false))); validateMethod.Statements.Add(checkLocationCountStatement); if (descriptor.NextMemberIndex > 0) { CodeConditionStatement generateNewOffset = new CodeConditionStatement(shouldCheckLocationCountExpression, new CodeStatement[] { new CodeAssignStatement(new CodeVariableReferenceExpression("offset"), new CodeBinaryOperatorExpression( new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("locationReferences"), "Count"), CodeBinaryOperatorType.Subtract, new CodePrimitiveExpression(descriptor.NextMemberIndex))) }); validateMethod.Statements.Add(generateNewOffset); } CodeAssignStatement setexpectedLocationsCountStatement = new CodeAssignStatement( new CodeVariableReferenceExpression("expectedLocationsCount"), new CodePrimitiveExpression(descriptor.NextMemberIndex)); validateMethod.Statements.Add(setexpectedLocationsCountStatement); foreach (KeyValuePair kvp in descriptor.Properties) { validateMethod.Statements.Add(GenerateLocationReferenceCheck(kvp.Value)); } foreach (KeyValuePair kvp in descriptor.Fields) { validateMethod.Statements.Add(GenerateLocationReferenceCheck(kvp.Value)); } if (this.compiledDataContexts.Count >= 1) { CompiledDataContextDescriptor baseDescriptor = this.compiledDataContexts.Peek(); CodeTypeDeclaration baseType = forReadOnly ? baseDescriptor.CodeTypeDeclarationForReadOnly : baseDescriptor.CodeTypeDeclaration; CodeMethodInvokeExpression invokeBase = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(baseType.Name), "Validate"), new CodeVariableReferenceExpression("locationReferences"), new CodePrimitiveExpression(false), new CodeVariableReferenceExpression("offset")); validateMethod.Statements.Add( new CodeMethodReturnStatement(invokeBase)); } else { validateMethod.Statements.Add( new CodeMethodReturnStatement( new CodePrimitiveExpression(true))); } if (forReadOnly) { descriptor.CodeTypeDeclarationForReadOnly.Members.Add(validateMethod); } else { descriptor.CodeTypeDeclaration.Members.Add(validateMethod); } } CodeConditionStatement GenerateLocationReferenceCheck(MemberData memberData) { CodeIndexerExpression indexer = new CodeIndexerExpression( new CodeVariableReferenceExpression("locationReferences"), new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("offset"), CodeBinaryOperatorType.Add, new CodePrimitiveExpression(memberData.Index))); CodeBinaryOperatorExpression locationNameExpression = new CodeBinaryOperatorExpression( new CodePropertyReferenceExpression(indexer, "Name"), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(memberData.Name)); CodeBinaryOperatorExpression locationTypeExpression = new CodeBinaryOperatorExpression( new CodePropertyReferenceExpression(indexer, "Type"), CodeBinaryOperatorType.IdentityInequality, new CodeTypeOfExpression(memberData.Type)); CodeBinaryOperatorExpression locationExpression = new CodeBinaryOperatorExpression( locationNameExpression, CodeBinaryOperatorType.BooleanOr, locationTypeExpression); CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(); returnStatement.Expression = new CodePrimitiveExpression(false); CodeConditionStatement locationStatement = new CodeConditionStatement( locationExpression, returnStatement); return locationStatement; } [Fx.Tag.SecurityNote(Critical = "Critical because we are accessing CodeDom.", Safe = "Safe because we are demanding FullTrust")] [SecuritySafeCritical] [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] void WriteCode(TextWriter textWriter) { using (CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider(this.settings.Language)) { using (IndentedTextWriter indentedTextWriter = new IndentedTextWriter(textWriter)) { codeDomProvider.GenerateCodeFromNamespace(this.codeNamespace, indentedTextWriter, new CodeGeneratorOptions()); } } } [Fx.Tag.SecurityNote(Critical = "Critical because we are using the CodeDomProvider class, which has a link demand for Full Trust.", Safe = "Safe because we are demanding FullTrust")] [SecuritySafeCritical] [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] TextExpressionCompilerResults CompileInMemory() { List messages = new List(); CompilerParameters compilerParameters = GetCompilerParameters(messages); CompilerResults compilerResults = null; using (CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider(this.settings.Language)) { compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, this.compileUnit); } TextExpressionCompilerResults results = new TextExpressionCompilerResults(); if (compilerResults.Errors == null || !compilerResults.Errors.HasErrors) { results.ResultType = compilerResults.CompiledAssembly.GetType(this.activityFullName); } results.HasSourceInfo = this.symbols != null; bool hasErrors = false; if (compilerResults.Errors != null && (compilerResults.Errors.HasWarnings || compilerResults.Errors.HasErrors)) { foreach (CompilerError ce in compilerResults.Errors) { TextExpressionCompilerError message = new TextExpressionCompilerError(); message.Message = ce.ErrorText; message.Number = ce.ErrorNumber; if (results.HasSourceInfo) { message.SourceLineNumber = ce.Line; } else { message.SourceLineNumber = -1; } message.IsWarning = ce.IsWarning; messages.Add(message); hasErrors |= !message.IsWarning; } } if (messages != null && messages.Count > 0) { results.SetMessages(messages, hasErrors); } return results; } [Fx.Tag.SecurityNote(Critical = "Critical because we are using the CompilerParameters class, which has a link demand for Full Trust.", Safe = "Safe because we are demanding FullTrust")] [SecuritySafeCritical] [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] CompilerParameters GetCompilerParameters(IList messages) { CompilerParameters compilerParameters = new CompilerParameters(); compilerParameters.GenerateExecutable = false; compilerParameters.GenerateInMemory = false; if (this.IsVB && !string.IsNullOrWhiteSpace(this.settings.RootNamespace)) { compilerParameters.CompilerOptions = string.Concat("/rootnamespace:", this.settings.RootNamespace); } List assemblies = this.settings.ForImplementation ? new List(TextExpression.GetReferencesForImplementation(this.settings.Activity)) : new List(TextExpression.GetReferences(this.settings.Activity)); assemblies.AddRange(TextExpression.DefaultReferences); foreach (AssemblyReference assemblyReference in assemblies) { if (assemblyReference == null) { continue; } assemblyReference.LoadAssembly(); if (assemblyReference.Assembly == null) { TextExpressionCompilerError warning = new TextExpressionCompilerError(); warning.IsWarning = true; warning.Message = SR.TextExpressionCompilerUnableToLoadAssembly(assemblyReference.AssemblyName); messages.Add(warning); continue; } if (assemblyReference.Assembly.CodeBase == null) { TextExpressionCompilerError warning = new TextExpressionCompilerError(); warning.IsWarning = true; warning.Message = SR.TextExpressionCompilerNoCodebase(assemblyReference.AssemblyName); messages.Add(warning); continue; } string fileName = new Uri(assemblyReference.Assembly.CodeBase).LocalPath; compilerParameters.ReferencedAssemblies.Add(fileName); } return compilerParameters; } void AlignText(Activity activity, ref string expressionText, out CodeLinePragma pragma) { pragma = null; if (this.symbols != null) { SourceLocation sourceLocation = null; Activity currentActivity = activity; while (currentActivity != null && !symbols.TryGetValue(currentActivity, out sourceLocation)) { currentActivity = currentActivity.Parent; } if (sourceLocation != null && !string.IsNullOrWhiteSpace(sourceLocation.FileName)) { int startLine = sourceLocation.StartLine; if (startLine > 1) { bool aligned = TryAlign(sourceLocation.StartColumn, ref expressionText); if (aligned) { // Alignment inserts an extra blank line at the beginning of the expression startLine--; } } pragma = new CodeLinePragma(sourceLocation.FileName, startLine); } } } bool TryAlign(int alignment, ref string expressionText) { if (!this.IsVB && !this.IsCS) { return false; } StringBuilder builder = new StringBuilder(); if (this.IsVB) { builder.Append('_'); } builder.Append(Environment.NewLine); builder.Append(new string(' ', alignment - 1)); builder.Append(expressionText); expressionText = builder.ToString(); return true; } CodeLinePragma GenerateLinePragmaForNamespace(string namespaceName) { if (this.fileName != null) { // if source xaml file doesn't exist or it doesn't contain TextExpression // it defaults to line number 1 int lineNumber = 1; Dictionary lineNumberDictionary = this.settings.ForImplementation ? this.lineNumbersForNSesForImpl : this.lineNumbersForNSes; int lineNumReturend; if (lineNumberDictionary.TryGetValue(namespaceName, out lineNumReturend)) { lineNumber = lineNumReturend; } return new CodeLinePragma(this.fileName, lineNumber); } return null; } bool TryGetSymbols(Activity rootActivity, out Dictionary symbols, out string fileName) { symbols = null; fileName = null; Activity implementationRoot = null; if (this.settings.ForImplementation) { // Regular compilation case via XamlBuildTask or for DynamicActivity // Debugger Symbols are attached to the first implementation child of rootActivity IEnumerable children = WorkflowInspectionServices.GetActivities(rootActivity); foreach (Activity child in children) { // Find the first implementation child of an activity if (child.Id.Contains(".")) { implementationRoot = child; break; } } } else { // XamlX case // Debugger Symbols are attached to WorkflowService.Body which is passed in the rootActivity implementationRoot = rootActivity; } if (implementationRoot == null) { return false; } try { string symbolString = DebugSymbol.GetSymbol(implementationRoot) as string; if (!string.IsNullOrEmpty(symbolString)) { WorkflowSymbol wfSymbol = WorkflowSymbol.Decode(symbolString); if (wfSymbol != null) { symbols = SourceLocationProvider.GetSourceLocations(rootActivity, wfSymbol); fileName = wfSymbol.FileName; return true; } } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } // Ignore invalid symbols. } return false; } string GetActivityFullName(TextExpressionCompilerSettings settings) { string rootNamespacePrefix = null; string namespacePrefix = null; string activityFullName = ""; if (this.IsVB && !String.IsNullOrWhiteSpace(this.settings.RootNamespace)) { rootNamespacePrefix = this.settings.RootNamespace + "."; } if (!String.IsNullOrWhiteSpace(this.settings.ActivityNamespace)) { namespacePrefix = this.settings.ActivityNamespace + "."; } if (rootNamespacePrefix != null) { if (namespacePrefix != null) { activityFullName = rootNamespacePrefix + namespacePrefix + settings.ActivityName; } else { activityFullName = rootNamespacePrefix + settings.ActivityName; } } else { if (namespacePrefix != null) { activityFullName = namespacePrefix + settings.ActivityName; } else { activityFullName = settings.ActivityName; } } return activityFullName; } class ExpressionCompilerActivityVisitor : CompiledExpressionActivityVisitor { TextExpressionCompiler compiler; public ExpressionCompilerActivityVisitor(TextExpressionCompiler compiler) { this.compiler = compiler; } public int NextExpressionId { get; set; } protected override void Visit(Activity activity, out bool exit) { base.Visit(activity, out exit); } protected override void VisitRoot(Activity activity, out bool exit) { this.compiler.OnRootActivity(); base.VisitRoot(activity, out exit); this.compiler.OnAfterRootActivity(); } protected override void VisitRootImplementationArguments(Activity activity, out bool exit) { base.VisitRootImplementationArguments(activity, out exit); if (this.ForImplementation) { this.compiler.OnAfterRootArguments(activity); } } protected override void VisitVariableScope(Activity activity, out bool exit) { this.compiler.OnVariableScope(activity); base.VisitVariableScope(activity, out exit); this.compiler.OnAfterVariableScope(); } protected override void VisitRootImplementationScope(Activity activity, out bool exit) { CompiledDataContextDescriptor rootArgumentAccessorContext = null; this.compiler.OnRootImplementationScope(activity, out rootArgumentAccessorContext); base.VisitRootImplementationScope(activity, out exit); this.compiler.OnAfterRootImplementationScope(activity, rootArgumentAccessorContext); } protected override void VisitVariableScopeArgument(RuntimeArgument runtimeArgument, out bool exit) { this.compiler.InVariableScopeArgument = true; base.VisitVariableScopeArgument(runtimeArgument, out exit); this.compiler.InVariableScopeArgument = false; } protected override void VisitITextExpression(Activity activity, out bool exit) { this.compiler.OnITextExpressionFound(activity, this); exit = false; } protected override void VisitDelegate(ActivityDelegate activityDelegate, out bool exit) { this.compiler.OnActivityDelegateScope(); base.VisitDelegate(activityDelegate, out exit); this.compiler.OnAfterActivityDelegateScope(); exit = false; } protected override void VisitDelegateArgument(RuntimeDelegateArgument delegateArgument, out bool exit) { this.compiler.OnDelegateArgument(delegateArgument); base.VisitDelegateArgument(delegateArgument, out exit); } } class CompiledExpressionDescriptor { internal bool IsValue { get { return !string.IsNullOrWhiteSpace(this.GetMethodName) && string.IsNullOrWhiteSpace(this.SetMethodName) && string.IsNullOrWhiteSpace(this.StatementMethodName); } } internal bool IsReference { get { return !string.IsNullOrWhiteSpace(this.SetMethodName); } } internal bool IsStatement { get { return !string.IsNullOrWhiteSpace(this.StatementMethodName); } } internal string TypeName { get; set; } internal Type ResultType { get; set; } internal string GetMethodName { get; set; } internal string SetMethodName { get; set; } internal string StatementMethodName { get; set; } internal int Id { get; set; } internal string ExpressionText { get; set; } internal string GetExpressionTreeMethodName { get; set; } } class CompiledDataContextDescriptor { IDictionary fields; IDictionary properties; ISet duplicates; Func isVb; public CompiledDataContextDescriptor(Func isVb) { this.isVb = isVb; } public IDictionary Fields { get { if (this.fields == null) { if (this.isVb()) { this.fields = new Dictionary(StringComparer.OrdinalIgnoreCase); } else { this.fields = new Dictionary(); } } return this.fields; } } public IDictionary Properties { get { if (this.properties == null) { if (this.isVb()) { this.properties = new Dictionary(StringComparer.OrdinalIgnoreCase); } else { this.properties = new Dictionary(); } } return this.properties; } } public ISet Duplicates { get { if (this.duplicates == null) { if (this.isVb()) { this.duplicates = new HashSet(StringComparer.OrdinalIgnoreCase); } else { this.duplicates = new HashSet(); } } return this.duplicates; } } public CodeTypeDeclaration CodeTypeDeclaration { get; set; } public CodeTypeDeclaration CodeTypeDeclarationForReadOnly { get; set; } public int NextMemberIndex { get; set; } } struct MemberData { public int Index; public string Name; public Type Type; } } }