File: Compilation\TemplateControlCodeDomTreeGenerator.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="TemplateControlCodeDomTreeGenerator.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.Compilation {
 
using System;
using System.Collections;
using System.Reflection;
using System.ComponentModel;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Globalization;
using System.Web.UI;
using System.Web.Util;
using Debug=System.Web.Util.Debug;
using System.Runtime.InteropServices;
using System.Collections.Generic;
 
internal abstract class TemplateControlCodeDomTreeGenerator : BaseTemplateCodeDomTreeGenerator {
 
    private const string stringResourcePointerName = "__stringResource";
 
    private TemplateControlParser _tcParser;
    private TemplateControlParser Parser { get { return _tcParser; } }
    private const string literalMemoryBlockName = "__literals";
 
    // This is used to detect incorrect base class in code beside scenarios.  See usage for details.
    internal const int badBaseClassLineMarker = 912304;
 
    internal TemplateControlCodeDomTreeGenerator(TemplateControlParser tcParser) : base(tcParser) {
        _tcParser = tcParser;
    }
 
    /*
     * Build the default constructor
     */
    protected override void BuildInitStatements(CodeStatementCollection trueStatements, CodeStatementCollection topLevelStatements) {
 
        base.BuildInitStatements(trueStatements, topLevelStatements);
 
        if (_stringResourceBuilder.HasStrings) {
            // e.g. private static object __stringResource;
            CodeMemberField stringResourcePointer = new CodeMemberField(typeof(Object), stringResourcePointerName);
            stringResourcePointer.Attributes |= MemberAttributes.Static;
            _sourceDataClass.Members.Add(stringResourcePointer);
 
            // e.g. __stringResource = TemplateControl.ReadStringResource(typeof(__GeneratedType));
            CodeAssignStatement readResource = new CodeAssignStatement();
            readResource.Left = new CodeFieldReferenceExpression(_classTypeExpr,
                                                             stringResourcePointerName);
            CodeMethodInvokeExpression methCallExpression = new CodeMethodInvokeExpression();
            methCallExpression.Method.TargetObject = new CodeThisReferenceExpression();
            methCallExpression.Method.MethodName = "ReadStringResource";
            readResource.Right = methCallExpression;
            trueStatements.Add(readResource);
        }
        //
        // Set the AppRelativeVirtualPath
        // e.g. ((System.Web.UI.Page)(this)).AppRelativeVirtualPath = "~/foo.aspx";
        // Note that we generate an artificial cast to cause a compile error if the base class
        // is incorrect (see below).
        //
        // Make sure the BuildAppRelativeVirtualPathProperty property is app independent, since
        // in precompilation scenarios, we can't make an assumption on the app name.
 
        // Use global:: to resolve types to avoid naming conflicts when user uses a class name
        // in the global namespace that already exists, such as Login or ReportViewer (DevDiv 79336)
        CodeTypeReference classTypeRef = CodeDomUtility.BuildGlobalCodeTypeReference(Parser.BaseType);
 
        CodeAssignStatement setProp = new CodeAssignStatement(
            new CodePropertyReferenceExpression(new CodeCastExpression(classTypeRef, new CodeThisReferenceExpression()), "AppRelativeVirtualPath"),
            new CodePrimitiveExpression(Parser.CurrentVirtualPath.AppRelativeVirtualPathString));
 
        // This line will fail to compile if the base class in the code beside is missing.  Set
        // a special line number on it to improve error handling (VSWhidbey 376977/468830)
        if (!_designerMode && Parser.CodeFileVirtualPath != null) {
            setProp.LinePragma = CreateCodeLinePragmaHelper(
                Parser.CodeFileVirtualPath.VirtualPathString, badBaseClassLineMarker);
        }
        topLevelStatements.Add(setProp);
    }
 
    /*
     * Build various properties, fields, methods
     */
    protected override void BuildMiscClassMembers() {
        base.BuildMiscClassMembers();
 
        // Build the automatic event hookup code
        if (!_designerMode)
            BuildAutomaticEventHookup();
 
        // Build the ApplicationInstance property
        BuildApplicationInstanceProperty();
 
        if (_designerMode) {
            GenerateDummyBindMethodsAtDesignTime();
        }
 
        BuildSourceDataTreeFromBuilder(Parser.RootBuilder,
            false /*fInTemplate*/, false /*topLevelTemplate*/, null /*pse*/);
 
        if (!_designerMode)
            BuildFrameworkInitializeMethod();
    }
 
    /*
     * Build the strongly typed new property
     */
    // e.g. public new {propertyType} Master { get { return ({propertyType})base.Master; } }
    internal void BuildStronglyTypedProperty(string propertyName, Type propertyType) {
        // VSWhidbey 321818.
        // overriding method with same name is not allowed using J#.
        if (_usingVJSCompiler) {
            return;
        }
 
        CodeMemberProperty prop = new CodeMemberProperty();
        prop.Attributes &= ~MemberAttributes.AccessMask;
        prop.Attributes &= ~MemberAttributes.ScopeMask;
        prop.Attributes |= MemberAttributes.Final | MemberAttributes.New | MemberAttributes.Public;
        prop.Name = propertyName;
        prop.Type = new CodeTypeReference(propertyType);
 
        CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression(
            new CodeBaseReferenceExpression(), propertyName);
 
        prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(propertyType, propRef)));
        _intermediateClass.Members.Add(prop);
    }
 
    private void GenerateDummyBindMethodsAtDesignTime() {
        // public string Bind(string expression,string format) {return String.Empty;}
        GenerateBindMethod(addFormatParameter: true);
        
        // public string Bind(string expression) {return String.Empty;}
        GenerateBindMethod(addFormatParameter: false);
    }
 
    private void GenerateBindMethod(bool addFormatParameter) {
        if (_sourceDataClass == null) {
            return;
        }
 
        CodeMemberMethod bindMethod = new CodeMemberMethod();
        bindMethod.Name = "Bind";
        bindMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "expression"));
        if (addFormatParameter) {
            bindMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "format"));
        }
        bindMethod.ReturnType = new CodeTypeReference(typeof(string));
        bindMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(String.Empty)));
        _sourceDataClass.Members.Add(bindMethod);
    }
 
    /*
     * Build the data tree for the FrameworkInitialize method
     */
    private void BuildFrameworkInitializeMethod() {
 
        // Skip if we're only generating the intermediate class
        if (_sourceDataClass == null)
            return;
 
        CodeMemberMethod method = new CodeMemberMethod();
        AddDebuggerNonUserCodeAttribute(method);
        method.Attributes &= ~MemberAttributes.AccessMask;
        method.Attributes &= ~MemberAttributes.ScopeMask;
        method.Attributes |= MemberAttributes.Override | MemberAttributes.Family;
        method.Name = "FrameworkInitialize";
 
        BuildFrameworkInitializeMethodContents(method);
 
        // This line will fail to compile if the base class in the code beside is incorrect.  Set
        // a special line number on it to improve error handling (VSWhidbey 376977/468830)
        if (!_designerMode && Parser.CodeFileVirtualPath != null) {
            method.LinePragma = CreateCodeLinePragmaHelper(
                Parser.CodeFileVirtualPath.VirtualPathString, badBaseClassLineMarker);
        }
 
        _sourceDataClass.Members.Add(method);
    }
 
    /*
     * Build the contents of the FrameworkInitialize method
     */
    protected virtual void BuildFrameworkInitializeMethodContents(CodeMemberMethod method) {
 
        // Call the base FrameworkInitialize
        CodeMethodInvokeExpression baseCallExpression = new CodeMethodInvokeExpression(
            new CodeBaseReferenceExpression(), method.Name);
        method.Statements.Add(new CodeExpressionStatement(baseCallExpression));
 
        // No strings: don't do anything
        if (_stringResourceBuilder.HasStrings) {
 
            // e.g. SetStringResourcePointer(__stringResource, 0);
            CodeMethodInvokeExpression methCallExpression = new CodeMethodInvokeExpression(
                new CodeThisReferenceExpression(), "SetStringResourcePointer");
            methCallExpression.Parameters.Add(new CodeFieldReferenceExpression(
                _classTypeExpr, stringResourcePointerName));
            // Pass 0 for the maxResourceOffset, since it's being ignored
            methCallExpression.Parameters.Add(new CodePrimitiveExpression(0));
            method.Statements.Add(new CodeExpressionStatement(methCallExpression));
        }
 
        CodeMethodInvokeExpression call = new CodeMethodInvokeExpression();
        call.Method.TargetObject = new CodeThisReferenceExpression();
        call.Method.MethodName = "__BuildControlTree";
        call.Parameters.Add(new CodeThisReferenceExpression());
        method.Statements.Add(new CodeExpressionStatement(call));
    }
 
    /*
     * Build the automatic event hookup code
     */
    private void BuildAutomaticEventHookup() {
 
        // Skip if we're only generating the intermediate class
        if (_sourceDataClass == null)
            return;
 
        CodeMemberProperty prop;
 
        // If FAutoEventWireup is turned off, generate a SupportAutoEvents prop that
        // returns false.
        if (!Parser.FAutoEventWireup) {
            prop = new CodeMemberProperty();
            prop.Attributes &= ~MemberAttributes.AccessMask;
            prop.Attributes &= ~MemberAttributes.ScopeMask;
            prop.Attributes |= MemberAttributes.Override | MemberAttributes.Family;
            prop.Name = "SupportAutoEvents";
            prop.Type = new CodeTypeReference(typeof(bool));
            prop.GetStatements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(false)));
            _sourceDataClass.Members.Add(prop);
            return;
        }
    }
 
    /*
     * Build the ApplicationInstance property
     */
    private void BuildApplicationInstanceProperty() {
 
        CodeMemberProperty prop;
 
        Type appType = BuildManager.GetGlobalAsaxType();
 
        prop = new CodeMemberProperty();
        prop.Attributes &= ~MemberAttributes.AccessMask;
        prop.Attributes &= ~MemberAttributes.ScopeMask;
        prop.Attributes |= MemberAttributes.Final | MemberAttributes.Family;
 
        if (_designerMode) {
            ApplyEditorBrowsableCustomAttribute(prop);
        }
 
        prop.Name = "ApplicationInstance";
        prop.Type = new CodeTypeReference(appType);
 
        CodePropertyReferenceExpression propRef = new CodePropertyReferenceExpression(
            new CodeThisReferenceExpression(), "Context");
        propRef = new CodePropertyReferenceExpression(propRef, "ApplicationInstance");
 
        prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(
            appType, propRef)));
        _intermediateClass.Members.Add(prop);
    }
 
        protected override void BuildDefaultConstructor() {
            base.BuildDefaultConstructor();
 
            if (BinaryCompatibility.Current.TargetsAtLeastFramework472 &&
                !(_designerMode || _sourceDataClass == null)) {
                foreach (var c in Parser.BaseType.GetConstructors(BindingFlags.Instance | BindingFlags.Public)) {
                    if (c.GetParameters().Length > 0) {
                        AddConstructorToSource(c);
                    }
                }
            }
        }
        
        private void AddConstructorToSource(ConstructorInfo ctor) {
            Debug.Assert(ctor != null);
            Debug.Assert(ctor.GetParameters().Length > 0);
 
            var ctorCode = new CodeConstructor();
            AddDebuggerNonUserCodeAttribute(ctorCode);
            ctorCode.Attributes &= ~MemberAttributes.AccessMask;
            ctorCode.Attributes |= MemberAttributes.Public;
 
            // copy all the attributes of the contrustor parameters
            foreach (var p in ctor.GetParameters()) {
                var parameterExpr = new CodeParameterDeclarationExpression(p.ParameterType, p.Name);
                foreach (var attr in p.CustomAttributes) {
                    var attrArgs = new List<CodeAttributeArgument>();
                    foreach (var arg in attr.ConstructorArguments) {
                        attrArgs.Add(new CodeAttributeArgument(new CodePrimitiveExpression(arg.Value)));
                    }
 
                    foreach (var arg in attr.NamedArguments) {
                        attrArgs.Add(new CodeAttributeArgument(arg.MemberName, new CodePrimitiveExpression(arg.TypedValue.Value)));
                    }
 
                    var customAttrExpr = new CodeAttributeDeclaration(new CodeTypeReference(attr.AttributeType), attrArgs.ToArray());
                    parameterExpr.CustomAttributes.Add(customAttrExpr);
                }
                if (p.HasDefaultValue) {
                    var defaultValAttrExpr = new CodeAttributeDeclaration(new CodeTypeReference(typeof(DefaultParameterValueAttribute)),
                        new CodeAttributeArgument(new CodePrimitiveExpression(p.DefaultValue)));
                    parameterExpr.CustomAttributes.Add(defaultValAttrExpr);
                }
                ctorCode.Parameters.Add(parameterExpr);
                ctorCode.BaseConstructorArgs.Add(new CodeVariableReferenceExpression(p.Name));
            }
 
            ctorCode.Statements.Add(CreateInitInvoke());
            _sourceDataClass.Members.Add(ctorCode);
        }
    }
}