//------------------------------------------------------------------------------
//
// 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