//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ /********************************************** Class hierarchy: BaseCodeDomTreeGenerator BaseTemplateCodeDomTreeGenerator TemplateControlCodeDomTreeGenerator PageCodeDomTreeGenerator UserControlCodeDomTreeGenerator PageThemeCodeDomTreeGenerator ApplicationFileCodeDomTreeGenerator ***********************************************/ namespace System.Web.Compilation { using System.Text; using System.Runtime.Serialization.Formatters; using System.ComponentModel; using System; using System.Collections; using System.Collections.Specialized; using System.Reflection; using System.IO; using Microsoft.Win32; using System.Security.Cryptography; using System.Web.Caching; using System.Web.Util; using System.Web.UI; using System.Web.SessionState; using System.CodeDom; using System.CodeDom.Compiler; using Util = System.Web.UI.Util; using System.Web.Hosting; using System.Web.Profile; using System.Web.Configuration; using System.Globalization; internal abstract class BaseCodeDomTreeGenerator { protected CodeDomProvider _codeDomProvider; protected CodeCompileUnit _codeCompileUnit; private CodeNamespace _sourceDataNamespace; protected CodeTypeDeclaration _sourceDataClass; protected CodeTypeDeclaration _intermediateClass; private CompilerParameters _compilParams; protected StringResourceBuilder _stringResourceBuilder; protected bool _usingVJSCompiler; private static IDictionary _generatedColumnOffsetDictionary; private VirtualPath _virtualPath; // The constructors protected CodeConstructor _ctor; protected CodeTypeReferenceExpression _classTypeExpr; internal const string defaultNamespace = "ASP"; // Used for things that we don't want the user to see internal const string internalAspNamespace = "__ASP"; private const string initializedFieldName = "__initialized"; private const string _dummyVariable = "__dummyVar"; private const int _defaultColumnOffset = 4; private TemplateParser _parser; TemplateParser Parser { get { return _parser; } } // We generate different code for the designer protected bool _designerMode; internal void SetDesignerMode() { _designerMode = true; } private IDictionary _linePragmasTable; internal IDictionary LinePragmasTable { get { return _linePragmasTable; } } // Used to generate indexed into the LinePragmasTable private int _pragmaIdGenerator=1; private static bool _urlLinePragmas; static BaseCodeDomTreeGenerator() { CompilationSection config = MTConfigUtil.GetCompilationAppConfig(); _urlLinePragmas = config.UrlLinePragmas; } #if DBG private bool _addedDebugComment; #endif #if DBG protected void AppendDebugComment(CodeStatementCollection statements) { if (!_addedDebugComment) { _addedDebugComment = true; StringBuilder debugComment = new StringBuilder(); debugComment.Append("\r\n"); debugComment.Append("** DEBUG INFORMATION **"); debugComment.Append("\r\n"); statements.Add(new CodeCommentStatement(debugComment.ToString())); } } #endif internal /*public*/ CodeCompileUnit GetCodeDomTree(CodeDomProvider codeDomProvider, StringResourceBuilder stringResourceBuilder, VirtualPath virtualPath) { Debug.Assert(_codeDomProvider == null && _stringResourceBuilder == null); _codeDomProvider = codeDomProvider; _stringResourceBuilder = stringResourceBuilder; _virtualPath = virtualPath; // Build the data tree that needs to be compiled if (!BuildSourceDataTree()) return null; // Tell the root builder that the CodeCompileUnit is now fully complete if (Parser.RootBuilder != null) { Parser.RootBuilder.OnCodeGenerationComplete(); } return _codeCompileUnit; } protected /*public*/ CompilerParameters CompilParams { get { return _compilParams; } } internal string GetInstantiatableFullTypeName() { // In updatable mode, we never build the final type, so return null if (PrecompilingForUpdatableDeployment) return null; return Util.MakeFullTypeName(_sourceDataNamespace.Name, _sourceDataClass.Name); } internal string GetIntermediateFullTypeName() { return Util.MakeFullTypeName(Parser.BaseTypeNamespace, _intermediateClass.Name); } /* * Set some fields that are needed for code generation */ protected BaseCodeDomTreeGenerator(TemplateParser parser) { _parser = parser; Debug.Assert(Parser.BaseType != null); } protected void ApplyEditorBrowsableCustomAttribute(CodeTypeMember member) { Debug.Assert(_designerMode, "This method should only be used in design mode."); // Generate EditorBrowsableAttribute to hide the generated methods from the tool // [EditorBrowsable(EditorBrowsableState.Never)] CodeAttributeDeclaration editorBrowsableAttribute = new CodeAttributeDeclaration(); editorBrowsableAttribute.Name = typeof(EditorBrowsableAttribute).FullName; editorBrowsableAttribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EditorBrowsableState)), "Never"))); member.CustomAttributes.Add(editorBrowsableAttribute); } /// /// Create a name for the generated class /// protected virtual string GetGeneratedClassName() { string className; // If the user specified the class name, just use that if (Parser.GeneratedClassName != null) return Parser.GeneratedClassName; // Use the input file name to generate the class name className = _virtualPath.FileName; // Prepend the class name with the directory path within the app (DevDiv 42063) string appRelVirtualDir = _virtualPath.Parent.AppRelativeVirtualPathStringOrNull; if (appRelVirtualDir != null) { Debug.Assert(UrlPath.IsAppRelativePath(appRelVirtualDir)); className = appRelVirtualDir.Substring(2) + className; } // Change invalid chars to underscores className = Util.MakeValidTypeNameFromString(className); // Make it lower case to make it more predictable (VSWhidbey 503369) className = className.ToLowerInvariant(); // If it's the same as the base type name, prepend it with an underscore to prevent // a compile error. string baseTypeName = Parser.BaseTypeName != null ? Parser.BaseTypeName : Parser.BaseType.Name; if (StringUtil.EqualsIgnoreCase(className, baseTypeName)) { className = "_" + className; } return className; } internal static bool IsAspNetNamespace(string ns) { return (ns == defaultNamespace); } private bool PrecompilingForUpdatableDeployment { get { // For global.asax, this never applies if (IsGlobalAsaxGenerator) return false; return BuildManager.PrecompilingForUpdatableDeployment; } } private bool BuildSourceDataTree() { _compilParams = Parser.CompilParams; _codeCompileUnit = new CodeCompileUnit(); _codeCompileUnit.UserData["AllowLateBound"] = !Parser.FStrict; _codeCompileUnit.UserData["RequireVariableDeclaration"] = Parser.FExplicit; // Set a flag indicating if we're using the VJS compiler. See comment in BuildExtractMethod for more information. _usingVJSCompiler = (_codeDomProvider.FileExtension == ".jsl"); _sourceDataNamespace = new CodeNamespace(Parser.GeneratedNamespace); string generatedClassName = GetGeneratedClassName(); if (Parser.BaseTypeName != null) { Debug.Assert(Parser.CodeFileVirtualPath != null); // This is the case where the page has a CodeFile attribute CodeNamespace intermediateNamespace = new CodeNamespace(Parser.BaseTypeNamespace); _codeCompileUnit.Namespaces.Add(intermediateNamespace); _intermediateClass = new CodeTypeDeclaration(Parser.BaseTypeName); // Specify the base class in the UserData in case the CodeDom provider needs // to reflect on it when generating code from the CodeCompileUnit (VSWhidbey 475294) // In design mode, use the default base type (e.g. Page or UserControl) to avoid // ending up with a type that can't be serialized to the Venus domain (VSWhidbey 545535) if (_designerMode) _intermediateClass.UserData["BaseClassDefinition"] = Parser.DefaultBaseType; else _intermediateClass.UserData["BaseClassDefinition"] = Parser.BaseType; intermediateNamespace.Types.Add(_intermediateClass); // Generate a partial class _intermediateClass.IsPartial = true; // Unless we're precompiling for updatable deployment, create the derived class if (!PrecompilingForUpdatableDeployment) { _sourceDataClass = new CodeTypeDeclaration(generatedClassName); // VSWhidbey 411701. Always use global type reference for the baseType // when codefile is present. _sourceDataClass.BaseTypes.Add(CodeDomUtility.BuildGlobalCodeTypeReference( Util.MakeFullTypeName(Parser.BaseTypeNamespace, Parser.BaseTypeName))); _sourceDataNamespace.Types.Add(_sourceDataClass); } } else { // The page is not using code besides _intermediateClass = new CodeTypeDeclaration(generatedClassName); _intermediateClass.BaseTypes.Add(CodeDomUtility.BuildGlobalCodeTypeReference(Parser.BaseType)); _sourceDataNamespace.Types.Add(_intermediateClass); // There is only one class, so make both fields point to the same thing _sourceDataClass = _intermediateClass; } // Add the derived class namespace after the base partial class so C# parser // can still parse the code correctly in case the derived class contains error. // VSWhidbey 397646 _codeCompileUnit.Namespaces.Add(_sourceDataNamespace); // We don't generate any code during updatable precompilation of a single (inline) page, // except for global.asax if (PrecompilingForUpdatableDeployment && Parser.CodeFileVirtualPath == null) return false; // Add metadata attributes to the class GenerateClassAttributes(); // In VB, always import Microsoft.VisualBasic (VSWhidbey 256475) if (_codeDomProvider is Microsoft.VisualBasic.VBCodeProvider) _sourceDataNamespace.Imports.Add(new CodeNamespaceImport("Microsoft.VisualBasic")); // Add all the namespaces if (Parser.NamespaceEntries != null) { foreach (NamespaceEntry entry in Parser.NamespaceEntries.Values) { // Create a line pragma if available CodeLinePragma linePragma; if (entry.VirtualPath != null) { linePragma = CreateCodeLinePragma(entry.VirtualPath, entry.Line); } else { linePragma = null; } CodeNamespaceImport nsi = new CodeNamespaceImport(entry.Namespace); nsi.LinePragma = linePragma; _sourceDataNamespace.Imports.Add(nsi); } } if (_sourceDataClass != null) { // We need to generate a global reference to avoid ambiguities (VSWhidbey 284936) string fullClassName = Util.MakeFullTypeName(_sourceDataNamespace.Name, _sourceDataClass.Name); CodeTypeReference classTypeRef = CodeDomUtility.BuildGlobalCodeTypeReference(fullClassName); // Since this is needed in several places, store it in a member variable _classTypeExpr = new CodeTypeReferenceExpression(classTypeRef); } // Add the implemented interfaces GenerateInterfaces(); // Build various properties, fields, methods BuildMiscClassMembers(); // Build the default constructors if (!_designerMode && _sourceDataClass != null) { _ctor = new CodeConstructor(); AddDebuggerNonUserCodeAttribute(_ctor); _sourceDataClass.Members.Add(_ctor); BuildDefaultConstructor(); } return true; } /* * Add metadata attributes to the class */ protected virtual void GenerateClassAttributes() { // If this is a debuggable page, generate a // CompilerGlobalScopeAttribute attribute (ASURT 33027) if (CompilParams.IncludeDebugInformation && _sourceDataClass != null) { CodeAttributeDeclaration attribDecl = new CodeAttributeDeclaration( "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute"); _sourceDataClass.CustomAttributes.Add(attribDecl); } } /* * Generate the list of implemented interfaces */ protected virtual void GenerateInterfaces() { if (Parser.ImplementedInterfaces != null) { foreach (Type t in Parser.ImplementedInterfaces) { _intermediateClass.BaseTypes.Add(new CodeTypeReference(t)); } } } /* * Build first-time intialization statements */ protected virtual void BuildInitStatements(CodeStatementCollection trueStatements, CodeStatementCollection topLevelStatements) { } /* * Build the default constructor */ protected virtual void BuildDefaultConstructor() { _ctor.Attributes &= ~MemberAttributes.AccessMask; _ctor.Attributes |= MemberAttributes.Public; // private static bool __initialized; CodeMemberField initializedField = new CodeMemberField(typeof(bool), initializedFieldName); initializedField.Attributes |= MemberAttributes.Static; _sourceDataClass.Members.Add(initializedField); // if (__intialized == false) CodeConditionStatement initializedCondition = new CodeConditionStatement(); initializedCondition.Condition = new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( _classTypeExpr, initializedFieldName), CodeBinaryOperatorType.ValueEquality, new CodePrimitiveExpression(false)); this.BuildInitStatements(initializedCondition.TrueStatements, _ctor.Statements); initializedCondition.TrueStatements.Add(new CodeAssignStatement( new CodeFieldReferenceExpression( _classTypeExpr, initializedFieldName), new CodePrimitiveExpression(true))); // i.e. __intialized = true; _ctor.Statements.Add(initializedCondition); } /* * Build various properties, fields, methods */ protected virtual void BuildMiscClassMembers() { // Build the Profile property if (NeedProfileProperty) BuildProfileProperty(); // Skip the rest if we're only generating the intermediate class if (_sourceDataClass == null) return; // Build the injected properties from the global.asax tags BuildApplicationObjectProperties(); BuildSessionObjectProperties(); // Build the injected properties for objects scoped to the page BuildPageObjectProperties(); // Add all the