|
//------------------------------------------------------------------------------
// <copyright file="TemplateParser.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* Implements the ASP.NET template parser
*
* Copyright (c) 1998 Microsoft Corporation
*/
// Turn this on to do regex profiling
//#define PROFILE_REGEX
namespace System.Web.UI {
using System.Runtime.Serialization.Formatters;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading;
using System.Reflection;
using System.Globalization;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web.Caching;
using System.Web.Util;
using System.Web.Hosting;
using System.Web.Compilation;
using HttpException = System.Web.HttpException;
using System.Text.RegularExpressions;
using System.Security.Permissions;
using System.Web.Configuration;
using System.Web.Instrumentation;
/// <internalonly/>
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public abstract class TemplateParser : BaseParser, IAssemblyDependencyParser {
internal const string CodeFileBaseClassAttributeName = "codefilebaseclass";
// The <compilation> config section
private CompilationSection _compConfig;
internal CompilationSection CompConfig {
get { return _compConfig; }
}
// The <pages> config section
private PagesSection _pagesConfig;
internal PagesSection PagesConfig {
get { return _pagesConfig; }
}
// const masks into the BitVector32
private const int isServerTag = 0x00000001;
private const int inScriptTag = 0x00000002;
private const int ignoreScriptTag = 0x00000004;
private const int ignoreNextSpaceString = 0x00000008;
internal const int requiresCompilation = 0x00000010; // Has constructs that require compilation
private const int ignoreControlProperties = 0x00000020;
internal const int aspCompatMode = 0x00000040;
private const int hasCodeBehind = 0x00000080;
private const int inDesigner = 0x00000100;
private const int ignoreParseErrors = 0x00000200;
private const int mainDirectiveSpecified = 0x00000400;
private const int mainDirectiveHandled = 0x00000800;
private const int useExplicit = 0x00001000;
private const int hasDebugAttribute = 0x00002000;
private const int debug = 0x00004000;
private const int noLinePragmas = 0x00008000;
private const int strict = 0x00010000;
internal const int noAutoEventWireup = 0x00020000;
private const int attemptedImplicitResources = 0x00040000;
internal const int buffer = 0x00080000;
internal const int requiresSessionState = 0x00100000;
internal const int readOnlySessionState = 0x00200000;
internal const int validateRequest = 0x00400000;
internal const int asyncMode = 0x00800000;
private const int throwOnFirstParseError = 0x01000000;
private const int ignoreParserFilter = 0x02000000;
internal const int calledFromParseControlFlag = 0x04000000;
#pragma warning disable 0649
internal SimpleBitVector32 flags;
#pragma warning restore 0649
private MainTagNameToTypeMapper _typeMapper;
internal MainTagNameToTypeMapper TypeMapper { get { return _typeMapper; } }
internal ICollection UserControlRegisterEntries { get { return TypeMapper.UserControlRegisterEntries; } }
internal List<TagNamespaceRegisterEntry> TagRegisterEntries { get { return TypeMapper.TagRegisterEntries; } }
private Stack _builderStack; // Stack of BuilderStackEntry's
internal Stack BuilderStack {
get {
EnsureRootBuilderCreated();
return _builderStack;
}
}
private string _id;
private StringSet _idList;
private Stack _idListStack;
private ScriptBlockData _currentScript;
private StringBuilder _literalBuilder;
// The line number in file currently being parsed
internal int _lineNumber;
// The line number at which the current script block started
private int _scriptStartLineNumber;
// String that contains the data to be parsed
private string _text;
public string Text {
get { return _text; }
internal set { _text = value; }
}
// The class from which to inherit if we are compiling a class
private Type _baseType;
internal Type BaseType {
get { return _baseType; }
set { _baseType = value; }
}
// The namespace and name of the class from which to inherit. Only used with code separation,
// since we don't have the live Type in that case (not yet compiled)
private string _baseTypeNamespace;
internal string BaseTypeNamespace { get { return _baseTypeNamespace; } }
private string _baseTypeName;
internal string BaseTypeName { get { return _baseTypeName; } }
internal bool IgnoreControlProperties {
get { return flags[ignoreControlProperties]; }
set { flags[ignoreControlProperties] = value; }
}
// Indicates whether the parser should throw on the first error.
internal bool ThrowOnFirstParseError {
get { return flags[throwOnFirstParseError]; }
set { flags[throwOnFirstParseError] = value; }
}
// The interfaces that we implement (ArrayList of Type objects)
private ArrayList _implementedInterfaces;
internal ArrayList ImplementedInterfaces { get { return _implementedInterfaces; } }
internal bool HasCodeBehind { get { return flags[hasCodeBehind]; } }
internal abstract Type DefaultBaseType { get; }
internal PageParserFilter _pageParserFilter;
private IImplicitResourceProvider _implicitResourceProvider;
// The FInDesigner property gets used by control builders so that
// they can behave differently if needed.
internal virtual bool FInDesigner {
get { return flags[inDesigner]; }
set { flags[inDesigner] = value; }
}
// When this is set, we ignore parse errors and keep on processing the page as
// well as possible. This is used for the Venus CBM scenario
internal virtual bool IgnoreParseErrors {
get { return flags[ignoreParseErrors]; }
set { flags[ignoreParseErrors] = value; }
}
// When true, it is not legal to have any constructs that require compilation
private CompilationMode _compilationMode;
internal CompilationMode CompilationMode {
get {
// When precompiling for deployment, always compile everything (VSWhidbey 266509)
if (BuildManager.PrecompilingForDeployment)
return CompilationMode.Always;
return _compilationMode;
}
set {
if (value == CompilationMode.Never && flags[requiresCompilation]) {
ProcessError(SR.GetString(SR.Compilmode_not_allowed));
}
_compilationMode = value;
}
}
private ParserErrorCollection _parserErrors;
private ParserErrorCollection ParserErrors {
get {
if (_parserErrors == null) {
_parserErrors = new ParserErrorCollection();
}
return _parserErrors;
}
}
private bool HasParserErrors {
get { return _parserErrors != null && _parserErrors.Count > 0; }
}
// Method to report parser errors.
protected void ProcessError(string message) {
// Ignore the errors if in that mode.
if (IgnoreParseErrors) {
return;
}
// Rethrow as innerexception if in that mode.
if (ThrowOnFirstParseError) {
throw new HttpException(message);
}
// otherwise add to the error collection with proper info.
ParserError parseError = new ParserError(message, CurrentVirtualPath, _lineNumber);
ParserErrors.Add(parseError);
// If there is a CBM callback, inform it of the error
BuildManager.ReportParseError(parseError);
}
// Method to report exception, this is called when external exceptions are caught in the parser.
protected void ProcessException(Exception ex) {
// Ignore the errors if in that mode.
if (IgnoreParseErrors) {
return;
}
// Rethrow as innerexception if in that mode or it is a compile exception.
if (ThrowOnFirstParseError || ex is HttpCompileException) {
if (ex is HttpParseException)
throw ex;
throw new HttpParseException(ex.Message, ex);
}
// If it is already a parser exception remember the location corresponding to
// the original error.
ParserError parseError;
HttpParseException hpe = ex as HttpParseException;
if (hpe != null) {
parseError = new ParserError(hpe.Message, hpe.VirtualPath, hpe.Line);
}
else {
parseError = new ParserError(ex.Message, CurrentVirtualPath, _lineNumber);
}
// Remember the original exception.
parseError.Exception = ex;
ParserErrors.Add(parseError);
// If there is a CBM callback, inform it of the error only if the HttpParseException comes
// from the current virtualpath. Since if the exception is thrown from parsing another file,
// it would have been reported already.
if (hpe == null || CurrentVirtualPath.Equals(hpe.VirtualPathObject)) {
BuildManager.ReportParseError(parseError);
}
}
// When true, there are construct that require compilation
internal virtual bool RequiresCompilation {
get {
// By default, require compilation. The Page parser overrides this
// and can allow no-compile pages depending on the page contents
return true;
}
}
internal virtual bool IsCodeAllowed {
get {
// If it's a no-compile page, code is not allowed
if (CompilationMode == CompilationMode.Never)
return false;
// Likewise, check if the PageParserFilter allows code
if (_pageParserFilter != null && !_pageParserFilter.AllowCode)
return false;
return true;
}
}
internal void EnsureCodeAllowed() {
// If it's a no-compile page, fail since there is code on it.
// Likewise if the PageParserFilter returns IsCodeAllowed == false
if (!IsCodeAllowed) {
ProcessError(SR.GetString(SR.Code_not_allowed));
}
// Remember the fact that this page MUST be compiled
flags[requiresCompilation] = true;
}
// This is called whenever we parse an attribute that requires compilation
internal void OnFoundAttributeRequiringCompilation(string attribName) {
// If compilation is not alowed, fail
if (!IsCodeAllowed) {
ProcessError(SR.GetString(SR.Attrib_not_allowed, attribName));
}
// Remember the fact that this page MUST be compiled
flags[requiresCompilation] = true;
}
// This is called whenever we parse a directive that requires compilation
internal void OnFoundDirectiveRequiringCompilation(string directiveName) {
// If compilation is not alowed, fail
if (!IsCodeAllowed) {
ProcessError(SR.GetString(SR.Directive_not_allowed, directiveName));
}
// Remember the fact that this page MUST be compiled
flags[requiresCompilation] = true;
}
// This is called whenever we parse an event attribute on a tag
internal void OnFoundEventHandler(string directiveName) {
// If compilation is not alowed, fail
if (!IsCodeAllowed) {
ProcessError(SR.GetString(SR.Event_not_allowed, directiveName));
}
// Remember the fact that this page MUST be compiled
flags[requiresCompilation] = true;
}
private IDesignerHost _designerHost;
private ITypeResolutionService _typeResolutionService;
internal IDesignerHost DesignerHost {
get {
Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
return _designerHost;
}
set {
Debug.Assert(FInDesigner, "DesignerHost should be accessed only when FInDesigner == true");
_designerHost = value;
_typeResolutionService = null;
if (_designerHost != null) {
_typeResolutionService = (ITypeResolutionService)_designerHost.GetService(typeof(ITypeResolutionService));
if (_typeResolutionService == null) {
throw new ArgumentException(SR.GetString(SR.TypeResService_Needed));
}
}
}
}
// true if we're parsing global.asax
internal virtual bool FApplicationFile { get { return false; } }
// The global delegate to use for the DataBind event on controls when
// the parser is run in design-mode.
private EventHandler _designTimeDataBindHandler;
internal EventHandler DesignTimeDataBindHandler {
get { return _designTimeDataBindHandler; }
set { _designTimeDataBindHandler = value; }
}
// Used to detect circular references
private StringSet _circularReferenceChecker;
// The set of assemblies that the build system is telling us we will be linked with
private ICollection _referencedAssemblies;
// The set of assemblies that this file is explicitly asking for
private AssemblySet _assemblyDependencies;
internal AssemblySet AssemblyDependencies {
get { return _assemblyDependencies; }
}
// The list of virtual paths to source files we are dependent on
private StringSet _sourceDependencies;
internal StringSet SourceDependencies {
get { return _sourceDependencies; }
}
// The collection of <object> tags with scope=session
internal HttpStaticObjectsCollection _sessionObjects;
internal HttpStaticObjectsCollection SessionObjects {
get { return _sessionObjects; }
}
// The collection of <object> tags with scope=application
internal HttpStaticObjectsCollection _applicationObjects;
internal HttpStaticObjectsCollection ApplicationObjects {
get { return _applicationObjects; }
}
// data that was obtained from parsing the input file
private RootBuilder _rootBuilder;
internal RootBuilder RootBuilder {
get {
EnsureRootBuilderCreated();
return _rootBuilder;
}
}
// Main directive attributes coming from config
internal IDictionary _mainDirectiveConfigSettings;
// <namespace name, NamespaceEntry>
private Hashtable _namespaceEntries;
internal Hashtable NamespaceEntries { get { return _namespaceEntries; } }
private CompilerType _compilerType;
internal CompilerType CompilerType { get { return _compilerType; } }
// the server side scripts (list of ScriptBlockData's)
private ArrayList _scriptList;
internal ArrayList ScriptList { get { return _scriptList; } }
// the hash code which determines the set of controls on the page
private HashCodeCombiner _typeHashCode = new HashCodeCombiner();
internal int TypeHashCode { get { return _typeHashCode.CombinedHash32; } }
// The <object> tags local to the page. Entries are ObjectTagBuilder's.
private ArrayList _pageObjectList;
internal ArrayList PageObjectList { get { return _pageObjectList; } }
// Record extra parse data
private ParseRecorder _parseRecorders = ParseRecorder.Null;
internal ParseRecorder ParseRecorders { get { return _parseRecorders; } }
// Data parsed from the directives
internal CompilerParameters CompilParams { get { return _compilerType.CompilerParameters; } }
internal bool FExplicit { get { return flags[useExplicit]; } }
internal bool FLinePragmas { get { return !flags[noLinePragmas]; } }
private int _warningLevel=-1;
private string _compilerOptions;
internal bool FStrict { get { return flags[strict]; } }
// File that we must be compiled with, aka code besides (optional)
private VirtualPath _codeFileVirtualPath;
internal VirtualPath CodeFileVirtualPath { get { return _codeFileVirtualPath; } }
// Name that the user wants to give to the generated class
private string _generatedClassName;
internal string GeneratedClassName { get { return _generatedClassName; } }
// Name that the user wants to give to the generated namespace
private string _generatedNamespace = null;
internal string GeneratedNamespace {
get {
// If no namespace was specified, use "ASP"
if (_generatedNamespace == null)
return BaseCodeDomTreeGenerator.defaultNamespace;
return _generatedNamespace;
}
}
private ControlBuilderInterceptor _controlBuilderInterceptor;
internal ControlBuilderInterceptor ControlBuilderInterceptor {
get {
if (_controlBuilderInterceptor == null && CompConfig != null && CompConfig.ControlBuilderInterceptorTypeInternal != null) {
_controlBuilderInterceptor = (ControlBuilderInterceptor) Activator.CreateInstance(CompConfig.ControlBuilderInterceptorTypeInternal);
}
return _controlBuilderInterceptor;
}
}
/// <devdoc>
/// Parse the input into a Control. This is used to parse in a control dynamically from some
/// textual content.
/// </devdoc>
internal static Control ParseControl(string content, VirtualPath virtualPath, bool ignoreFilter) {
if (content == null) {
return null;
}
ITemplate t = ParseTemplate(content, virtualPath, ignoreFilter);
// Create a parent control to hold the controls we parsed
Control c = new Control();
t.InstantiateIn(c);
return c;
}
public static ITemplate ParseTemplate(string content, string virtualPath, bool ignoreFilter) {
return ParseTemplate(content, VirtualPath.Create(virtualPath), ignoreFilter);
}
private static ITemplate ParseTemplate(string content, VirtualPath virtualPath, bool ignoreFilter) {
TemplateParser parser = new UserControlParser();
return parser.ParseTemplateInternal(content, virtualPath, ignoreFilter);
}
private ITemplate ParseTemplateInternal(string content, VirtualPath virtualPath, bool ignoreFilter) {
// Use the passed in virtualPath, since we need to have one, and the content string
// itself doesn't have one.
CurrentVirtualPath = virtualPath;
CompilationMode = CompilationMode.Never;
_text = content;
// Ignore the PageParserFilter when processing ParserControl/ParseTemplate (VSWhidbey 361509)
// Allow the ignore action to be controlled by a parameter (DevDiv 38679)
flags[ignoreParserFilter] = ignoreFilter;
flags[calledFromParseControlFlag] = true;
Parse();
Debug.Assert(RootBuilder != null);
return RootBuilder;
}
/*
* Do some initialization before the parsing
*/
internal virtual void PrepareParse() {
if (_circularReferenceChecker == null)
_circularReferenceChecker = new CaseInsensitiveStringSet();
_baseType = DefaultBaseType;
// Initialize the main directive
_mainDirectiveConfigSettings = CreateEmptyAttributeBag();
// Get the config sections we care about
if (!FInDesigner) {
_compConfig = MTConfigUtil.GetCompilationConfig(CurrentVirtualPath);
_pagesConfig = MTConfigUtil.GetPagesConfig(CurrentVirtualPath);
}
// Get default settings from config
ProcessConfigSettings();
// Initialize the type mapper
// This must follow processing of config, so it can use the results
_typeMapper = new MainTagNameToTypeMapper(this as BaseTemplateParser);
// Register the <object> tag
_typeMapper.RegisterTag("object", typeof(System.Web.UI.ObjectTag));
_sourceDependencies = new CaseInsensitiveStringSet();
// Create and seed the stack of ID lists.
_idListStack = new Stack();
_idList = new CaseInsensitiveStringSet();
_scriptList = new ArrayList();
// Optionally collect additional parse data for render tracing
InitializeParseRecorders();
}
private void InitializeParseRecorders() {
if (FInDesigner)
return;
_parseRecorders = ParseRecorder.CreateRecorders(this);
}
private void EnsureRootBuilderCreated() {
// Create it on demand
if (_rootBuilder != null)
return;
if (BaseType == DefaultBaseType) {
// If the base type is the default, no need to look up the attribute
_rootBuilder = CreateDefaultFileLevelBuilder();
}
else {
// Look for a custom attribute
Type fileLevelBuilderType = GetFileLevelControlBuilderType();
if (fileLevelBuilderType == null) {
// No custom type: use the default
_rootBuilder = CreateDefaultFileLevelBuilder();
}
else {
// Create the custom file level builder
_rootBuilder = (RootBuilder) HttpRuntime.CreateNonPublicInstance(
fileLevelBuilderType);
}
}
_rootBuilder.Line = 1;
_rootBuilder.Init(this, null, null, null, null, null);
_rootBuilder.SetTypeMapper(TypeMapper);
_rootBuilder.VirtualPath = CurrentVirtualPath;
// Create and seed the stack of builders.
_builderStack = new Stack();
_builderStack.Push(new BuilderStackEntry(RootBuilder, null, null, 0, null, 0));
}
internal virtual Type DefaultFileLevelBuilderType {
get {
return typeof(RootBuilder);
}
}
internal virtual RootBuilder CreateDefaultFileLevelBuilder() {
// By default, create a RootBuilder
return new RootBuilder();
}
private Type GetFileLevelControlBuilderType() {
// Check whether the control's class exposes a custom file level builder type
FileLevelControlBuilderAttribute cba = null;
object[] attrs = BaseType.GetCustomAttributes(
typeof(FileLevelControlBuilderAttribute), /*inherit*/ true);
if ((attrs != null) && (attrs.Length > 0)) {
Debug.Assert(attrs[0] is FileLevelControlBuilderAttribute);
cba = (FileLevelControlBuilderAttribute)attrs[0];
}
if (cba == null)
return null;
// Make sure the type has the correct base class
Util.CheckAssignableType(DefaultFileLevelBuilderType, cba.BuilderType);
return cba.BuilderType;
}
// Get default settings from config
internal virtual void ProcessConfigSettings() {
if (_compConfig != null) {
flags[useExplicit] = _compConfig.Explicit;
flags[strict] = _compConfig.Strict;
}
if (PagesConfig != null) {
_namespaceEntries = PagesConfig.Namespaces.NamespaceEntries;
// Clone it so we don't modify the config settings
if (_namespaceEntries != null)
_namespaceEntries = (Hashtable) _namespaceEntries.Clone();
if (!flags[ignoreParserFilter]) {
// Check if a filter is registered, and if so initialize it
_pageParserFilter = PageParserFilter.Create(PagesConfig, CurrentVirtualPath, this);
}
}
}
internal void Parse(ICollection referencedAssemblies, VirtualPath virtualPath) {
_referencedAssemblies = referencedAssemblies;
CurrentVirtualPath = virtualPath;
Parse();
}
/*
* Parse the input
*/
internal void Parse() {
// Always set the culture to Invariant when parsing (ASURT 99071)
Thread currentThread = Thread.CurrentThread;
CultureInfo prevCulture = currentThread.CurrentCulture;
System.Web.Util.Debug.Trace("Culture", "Before parsing, culture is " + prevCulture.DisplayName);
currentThread.CurrentCulture = CultureInfo.InvariantCulture;
try {
try {
// Do some initialization before the parsing
PrepareParse();
ParseInternal();
HandlePostParse();
}
finally {
// Restore the previous culture
System.Web.Util.Debug.Trace("Culture", "After parsing, culture is " + currentThread.CurrentCulture.DisplayName);
currentThread.CurrentCulture = prevCulture;
System.Web.Util.Debug.Trace("Culture", "Restored culture to " + prevCulture.DisplayName);
}
}
catch { throw; } // Prevent Exception Filter Security Issue (ASURT 122835)
}
internal virtual void ParseInternal() {
// Parse either the file or string
if (_text != null) {
ParseString(_text, CurrentVirtualPath, Encoding.UTF8);
}
else {
AddSourceDependency(CurrentVirtualPath);
ParseFile(null /*physicalPath*/, CurrentVirtualPath.VirtualPathString);
}
}
internal TemplateParser() {
ThrowOnFirstParseError = true;
}
/*
* Parse the contents of the input file
*/
protected void ParseFile(string physicalPath, string virtualPath) {
ParseFile(physicalPath, VirtualPath.Create(virtualPath));
}
internal void ParseFile(string physicalPath, VirtualPath virtualPath) {
// Determine the file used for the circular references checker. Normally,
// we use the virtualPath, but we use the physical path if it specified,
// as is the case for <!-- #include file="foo.inc" -->
string fileToReferenceCheck = physicalPath != null ? physicalPath : virtualPath.VirtualPathString;
// Check for circular references of include files
if (_circularReferenceChecker.Contains(fileToReferenceCheck)) {
ProcessError(SR.GetString(SR.Circular_include));
return;
}
// Add the current file to the circular references checker.
_circularReferenceChecker.Add(fileToReferenceCheck);
try {
// Open a TextReader either from the physical or virtual path
StreamReader reader;
if (physicalPath != null) {
using (reader = Util.ReaderFromFile(physicalPath, CurrentVirtualPath)) {
ParseReader(reader, virtualPath);
}
}
else {
// Open a TextReader for the virtualPath we're parsing
using (Stream stream = virtualPath.OpenFile()) {
reader = Util.ReaderFromStream(stream, CurrentVirtualPath);
ParseReader(reader, virtualPath);
}
}
}
finally {
// Remove the current file from the circular references checker
_circularReferenceChecker.Remove(fileToReferenceCheck);
}
}
/*
* Parse the contents of the TextReader
*/
private void ParseReader(StreamReader reader, VirtualPath virtualPath) {
string s = reader.ReadToEnd();
// Save the text of the input file in case it's trivial
_text = s;
ParseString(s, virtualPath, reader.CurrentEncoding);
}
private void AddLiteral(string literal) {
if (_literalBuilder == null)
_literalBuilder = new StringBuilder();
_literalBuilder.Append(literal);
}
private string GetLiteral() {
if (_literalBuilder == null)
return null;
return _literalBuilder.ToString();
}
/*
* Update the hash code of the Type we're creating by xor'ing it with
* a string.
*/
internal void UpdateTypeHashCode(string text) {
_typeHashCode.AddObject(text);
}
/*
* Parse the contents of the string, and catch exceptions
*/
internal void ParseString(string text, VirtualPath virtualPath, Encoding fileEncoding) {
System.Web.Util.Debug.Trace("Template", "Starting parse at " + DateTime.Now);
// Save the previous base dirs and line number
VirtualPath prevVirtualPath = CurrentVirtualPath;
int prevLineNumber = _lineNumber;
// Set the new current base dirs and line number
CurrentVirtualPath = virtualPath;
_lineNumber = 1;
// Always ignore the spaces at the beginning of a string
flags[ignoreNextSpaceString] = true;
try {
ParseStringInternal(text, fileEncoding);
// If there are parser errors caught in the parser
if (HasParserErrors) {
ParserError firstError = ParserErrors[0];
Exception originalException = firstError.Exception;
// Use the first error as the inner exception if not already caught one.
if (originalException == null) {
originalException = new HttpException(firstError.ErrorText);
}
// Make it a HttpParseException with proper info.
HttpParseException ex = new HttpParseException(firstError.ErrorText,
originalException, firstError.VirtualPath, Text, firstError.Line);
// Add the rest of the errors
for (int i = 1; i < ParserErrors.Count; i++) {
ex.ParserErrors.Add(ParserErrors[i]);
}
// throw the new exception
throw ex;
}
// Make sure that if any code calls ProcessError/ProcessException after this point,
// it throws the error right away, since we won't look at ParserErrors/_firstParseException
// anymore
ThrowOnFirstParseError = true;
}
catch (Exception e) {
ErrorFormatter errorFormatter = null;
PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_PRE_PROCESSING);
PerfCounters.IncrementCounter(AppPerfCounter.ERRORS_TOTAL);
// Check if the exception has a formatter
errorFormatter = HttpException.GetErrorFormatter(e);
// If it doesn't, throw a parse exception
if (errorFormatter == null) {
throw new HttpParseException(e.Message, e,
CurrentVirtualPath, text, _lineNumber);
}
else {
// Otherwise, just rethrow it
throw;
}
}
finally {
// Restore the previous base dirs and line number
CurrentVirtualPath = prevVirtualPath;
_lineNumber = prevLineNumber;
}
System.Web.Util.Debug.Trace("Template", "Ending parse at " + DateTime.Now);
}
#if PROFILE_REGEX
private Match RunTagRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return TagRegex.Match(text, textPos);
}
private Match RunDirectiveRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return directiveRegex.Match(text, textPos);
}
private Match RunEndTagRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return endtagRegex.Match(text, textPos);
}
private Match RunCodeBlockRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return aspCodeRegex.Match(text, textPos);
}
private Match RunExprCodeBlockRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return aspExprRegex.Match(text, textPos);
}
private Match RunCommentRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return commentRegex.Match(text, textPos);
}
private Match RunIncludeRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return includeRegex.Match(text, textPos);
}
private Match RunTextRegex(string text, int textPos) {
int i=1;
if (i==0)
throw new HttpException("Bogus exception just to prevent method inlining");
return textRegex.Match(text, textPos);
}
#endif // PROFILE_REGEX
/*
* Parse the contents of the string
*/
private void ParseStringInternal(string text, Encoding fileEncoding) {
int textPos = 0;
// Find the last '>' in the input string
int lastGTIndex = text.LastIndexOf('>');
Regex tagRegex = TagRegex;
for (;;) {
Match match;
// 1: scan for text up to the next tag.
#if PROFILE_REGEX
if ((match = RunTextRegex(text, textPos)).Success)
#else
if ((match = textRegex.Match(text, textPos)).Success)
#endif
{
// Append the text to the literal builder
AddLiteral(match.ToString());
_lineNumber += Util.LineCount(text, textPos,
match.Index + match.Length);
textPos = match.Index + match.Length;
}
// we might be done now
if (textPos == text.Length)
break;
// 2: handle constructs that start with <
// This later gets set to true if we match a regex, but do not
// process the match
bool fMatchedButNotProcessed = false;
// Check to see if it's a directive (i.e. <%@ %> block)
if (!flags[inScriptTag] &&
#if PROFILE_REGEX
(match = RunDirectiveRegex(text, textPos)).Success)
#else
(match = directiveRegex.Match(text, textPos)).Success)
#endif
{
ProcessLiteral();
// Get all the directives into a bag
ParsedAttributeCollection directive;
string duplicateAttribute;
string directiveName = ProcessAttributes(text, match, out directive, true, out duplicateAttribute);
try {
// If there is a parser filter, give it a chance to look at the directive
PreprocessDirective(directiveName, directive);
ProcessDirective(directiveName, directive);
}
catch(Exception e) {
ProcessException(e);
}
// If we just found the main directive, and it uses a codeFile, check if we need to create
// a modified version of the file (used for updatable deployment precompilation)
if (directiveName.Length == 0 && _codeFileVirtualPath != null) {
CreateModifiedMainDirectiveFileIfNeeded(text, match, directive, fileEncoding);
}
// Always ignore the spaces after a directive
flags[ignoreNextSpaceString] = true;
}
// Check to see if it's a server side include
// e.g. <!-- #include file="foo.inc" -->
#if PROFILE_REGEX
else if ((match = RunIncludeRegex(text, textPos)).Success)
#else
else if ((match = includeRegex.Match(text, textPos)).Success)
#endif
{
try {
ProcessServerInclude(match);
}
catch(Exception ex) {
ProcessException(ex);
}
}
// Check to see if it's a comment <%-- --%> block
#if PROFILE_REGEX
else if ((match = RunCommentRegex(text, textPos)).Success)
#else
else if ((match = commentRegex.Match(text, textPos)).Success)
#endif
{
// Just skip it
}
// Check to see if it's an expression code block (i.e. <%= ... %> block)
else if (!flags[inScriptTag] &&
#if PROFILE_REGEX
(match = RunExprCodeBlockRegex(text, textPos)).Success)
#else
(match = aspExprRegex.Match(text, textPos)).Success)
#endif
{
ProcessCodeBlock(match, CodeBlockType.Expression, text);
}
// Check to see if it's an encoded expression code block (i.e. <%: ... %> block)
else if (!flags[inScriptTag] && (match = aspEncodedExprRegex.Match(text, textPos)).Success) {
ProcessCodeBlock(match, CodeBlockType.EncodedExpression, text);
}
// Check to see if it's a databinding expression block (i.e. <%# ... %> block)
// This does not include <%# %> blocks used as values for
// attributes of server tags.
else if (!flags[inScriptTag] &&
(match = databindExprRegex.Match(text, textPos)).Success) {
ProcessCodeBlock(match, CodeBlockType.DataBinding, text);
}
// Check to see if it's a code block (<% ... %>)
else if (!flags[inScriptTag] &&
#if PROFILE_REGEX
(match = RunCodeBlockRegex(text, textPos)).Success)
#else
(match = aspCodeRegex.Match(text, textPos)).Success)
#endif
{
string code = match.Groups["code"].Value.Trim();
if (code.StartsWith("$", StringComparison.Ordinal)) {
ProcessError(SR.GetString(SR.ExpressionBuilder_LiteralExpressionsNotAllowed, match.ToString(), code));
}
else {
ProcessCodeBlock(match, CodeBlockType.Code, text);
}
}
// Check to see if it's a tag. Don't run the tag regex if there is no '>' after the
// current position, since we know it cannot match, and it can take an exponential
// amount of time to run (VSWhidbey 141878,358072)
else if (!flags[inScriptTag] &&
#if PROFILE_REGEX
(match = RunTagRegex(text, textPos)).Success)
#else
lastGTIndex > textPos && (match = tagRegex.Match(text, textPos)).Success)
#endif
{
try {
if (!ProcessBeginTag(match, text))
fMatchedButNotProcessed = true;
}
catch (Exception ex) {
ProcessException(ex);
}
}
// Check to see if it's an end tag
#if PROFILE_REGEX
else if ((match = RunEndTagRegex(text, textPos)).Success)
#else
else if ((match = endtagRegex.Match(text, textPos)).Success)
#endif
{
if (!ProcessEndTag(match))
fMatchedButNotProcessed = true;
}
// Did we process the block that started with a '<'?
if (match == null || !match.Success || fMatchedButNotProcessed) {
// If we could not match the '<' at all, check for some
// specific syntax errors
if (!fMatchedButNotProcessed && !flags[inScriptTag])
DetectSpecialServerTagError(text, textPos);
// Skip the '<'
textPos++;
AddLiteral("<");
}
else {
_lineNumber += Util.LineCount(text, textPos,
match.Index + match.Length);
textPos = match.Index + match.Length;
}
// we might be done now
if (textPos == text.Length)
break;
}
if (flags[inScriptTag] && !IgnoreParseErrors) {
// Change the line number to where the script tag started to get
// the correct error message (ASURT 13698).
_lineNumber = _scriptStartLineNumber;
ProcessError(SR.GetString(SR.Unexpected_eof_looking_for_tag, "script"));
return;
}
// Process the final literal (if any)
ProcessLiteral();
}
// Used for updatable deployment precompilation
void CreateModifiedMainDirectiveFileIfNeeded(string text, Match match,
ParsedAttributeCollection mainDirective, Encoding fileEncoding) {
TextWriter precompTargetWriter = BuildManager.GetUpdatableDeploymentTargetWriter(CurrentVirtualPath, fileEncoding);
// If we're not precompiling for deployment, there is nothing to do here
if (precompTargetWriter == null)
return;
using (precompTargetWriter) {
// Write out everything up to the main directive
precompTargetWriter.Write(text.Substring(0, match.Index));
precompTargetWriter.Write("<%@ " + DefaultDirectiveName);
// Go through all the attributes on the main directive
foreach (DictionaryEntry entry in mainDirective) {
string attribName = (string) entry.Key;
string attribValue = (string) entry.Value;
// Remove the codefile and CodeFileBaseClass attributes
if (StringUtil.EqualsIgnoreCase(attribName, "codefile")) continue;
if (StringUtil.EqualsIgnoreCase(attribName, CodeFileBaseClassAttributeName)) continue;
// Write out a special token for the inherits attribute. It will later be replaced by
// the full type later in the precompilation. We can't do it here because we don't know
// the assembly name yet (VSWhidbey 467936)
if (StringUtil.EqualsIgnoreCase(attribName, "inherits")) {
attribValue = BuildManager.UpdatableInheritReplacementToken;
}
precompTargetWriter.Write(" ");
precompTargetWriter.Write(attribName);
precompTargetWriter.Write("=\"");
precompTargetWriter.Write(attribValue);
precompTargetWriter.Write("\"");
}
precompTargetWriter.Write(" %>");
// Write out everything after the main directive
precompTargetWriter.Write(text.Substring(match.Index+match.Length));
}
}
/*
* Do what needs to be done before returning after the parsing is complete
*/
internal virtual void HandlePostParse() {
// If there was no main directive in the page, process settings that may have come from config
if (!flags[mainDirectiveHandled]) {
ProcessMainDirective(_mainDirectiveConfigSettings);
flags[mainDirectiveHandled] = true;
}
// We need to check the PageParserFilter here to handle the case where the base was specified
// in web.config, and was *not* overridden with an 'inherits' attribute.
if (_pageParserFilter != null) {
if (!_pageParserFilter.AllowBaseType(BaseType)) {
throw new HttpException(
SR.GetString(SR.Base_type_not_allowed, BaseType.FullName));
}
}
// If there is more than one builder on the stack, some tag was
// not correctly closed, which is an error.
if (BuilderStack.Count > 1) {
BuilderStackEntry entry = (BuilderStackEntry) _builderStack.Peek();
string message = SR.GetString(SR.Unexpected_eof_looking_for_tag, entry._tagName);
ProcessException(new HttpParseException(message, null, entry.VirtualPath, entry._inputText, entry.Line));
return;
}
// If no language was specified in the page
if (_compilerType == null) {
if (!FInDesigner) {
// Get a default from config
_compilerType = CompilationUtil.GetDefaultLanguageCompilerInfo(
_compConfig, CurrentVirtualPath);
}
else {
// Get default from code
_compilerType = CompilationUtil.GetCodeDefaultLanguageCompilerInfo();
}
}
CompilerParameters compilParams = _compilerType.CompilerParameters;
// Override certain settings if they were specified on the page
if (flags[hasDebugAttribute])
compilParams.IncludeDebugInformation = flags[debug];
// Debugging requires medium trust level
if (compilParams.IncludeDebugInformation)
HttpRuntime.CheckAspNetHostingPermission(AspNetHostingPermissionLevel.Medium, SR.Debugging_not_supported_in_low_trust);
// If warningLevel was specified in the page, use it
if (_warningLevel >= 0) {
compilParams.WarningLevel = _warningLevel;
compilParams.TreatWarningsAsErrors = (_warningLevel>0);
}
if (_compilerOptions != null)
compilParams.CompilerOptions = _compilerOptions;
// Tell the filter (if any) that the parsing is complete
if (_pageParserFilter != null)
_pageParserFilter.ParseComplete(RootBuilder);
// Tell the ParseRecorders that parsing is complete
ParseRecorders.ParseComplete(RootBuilder);
}
/*
* Process all the text in the literal StringBuilder, and reset it
*/
private void ProcessLiteral() {
// Debug.Trace("Template", "Literal text: \"" + _literalBuilder.ToString() + "\"");
// Get the current literal string
string literal = GetLiteral();
// Nothing to do if it's empty
if (String.IsNullOrEmpty(literal)) {
flags[ignoreNextSpaceString] = false;
return;
}
// In global.asax, we don't allow random rendering content
if (FApplicationFile) {
// Make sure the literal is just white spaces
int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(literal);
// Only move the line number if not ignore parser error, otherwise the linenumber
// of other valid elements will be off.
if (iFirstNonWhiteSpace >= 0 && !IgnoreParseErrors) {
// Move the line number back to the first non-whitespace
_lineNumber -= Util.LineCount(literal, iFirstNonWhiteSpace, literal.Length);
ProcessError(SR.GetString(SR.Invalid_app_file_content));
}
}
else {
// Check if we should ignore the string (ASURT 8186)
bool fIgnoreThisLiteral = false;
if (flags[ignoreNextSpaceString]) {
flags[ignoreNextSpaceString] = false;
if (Util.IsWhiteSpaceString(literal))
fIgnoreThisLiteral = true;
}
if (!fIgnoreThisLiteral) {
// Process the settings that may come from config when the first non-trivial literal is parsed. VS Whidbey 141882
if (!flags[mainDirectiveHandled]) {
ProcessMainDirective(_mainDirectiveConfigSettings);
flags[mainDirectiveHandled] = true;
}
// Add it to the top builder
ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
try {
builder.AppendLiteralString(literal);
}
catch (Exception e) {
if (!IgnoreParseErrors) {
// If there was an error during the parsing of the literal, move
// the line number back to the beginning of the literal
int iFirstNonWhiteSpace = Util.FirstNonWhiteSpaceIndex(literal);
if (iFirstNonWhiteSpace < 0) iFirstNonWhiteSpace = 0;
_lineNumber -= Util.LineCount(literal, iFirstNonWhiteSpace, literal.Length);
ProcessException(e);
}
}
// Update the hash code with a fixed string, to mark that there is
// a literal, but allow it to change without affecting the hash.
UpdateTypeHashCode("string");
}
}
// Reset the StringBuilder for the next literal
_literalBuilder = null;
}
/*
* Process a server side SCRIPT tag
*/
private void ProcessServerScript() {
// Get the contents of the script tag
string script = GetLiteral();
// Nothing to do if it's empty.
// Unless we're in GenerateCodeCompileUnit mode, in which case
// we want the empty block (VSWhidbey 112777)
if (String.IsNullOrEmpty(script)) {
if (!IgnoreParseErrors)
return;
script = String.Empty;
}
// Add this script to the script builder, unless we're
// supposed to ignore it
if (!flags[ignoreScriptTag]) {
// First, give the PageParserFilter a chance to handle the code block
if (!PageParserFilterProcessedCodeBlock(CodeConstructType.ScriptTag, script, _currentScript.Line)) {
// Make sure it's legal to have code in this page
EnsureCodeAllowed();
_currentScript.Script = script;
_scriptList.Add(_currentScript);
_currentScript = null;
}
}
// Reset the StringBuilder for the next literal
_literalBuilder = null;
}
internal virtual void CheckObjectTagScope(ref ObjectTagScope scope) {
// Map the default scope to Page
if (scope == ObjectTagScope.Default)
scope = ObjectTagScope.Page;
// Check for invalid scopes
if (scope != ObjectTagScope.Page) {
throw new HttpException(
SR.GetString(SR.App_session_only_valid_in_global_asax));
}
}
/*
* Process an Object tag, depending on its scope
*/
private void ProcessObjectTag(ObjectTagBuilder objectBuilder) {
ObjectTagScope scope = objectBuilder.Scope;
CheckObjectTagScope(ref scope);
// Page and AppInstance are treated identically
if (scope == ObjectTagScope.Page ||
scope == ObjectTagScope.AppInstance) {
if (_pageObjectList == null)
_pageObjectList = new ArrayList();
_pageObjectList.Add(objectBuilder);
}
else if (scope == ObjectTagScope.Session) {
if (_sessionObjects == null)
_sessionObjects = new HttpStaticObjectsCollection();
_sessionObjects.Add(objectBuilder.ID,
objectBuilder.ObjectType,
objectBuilder.LateBound);
}
else if (scope == ObjectTagScope.Application) {
if (_applicationObjects == null)
_applicationObjects = new HttpStaticObjectsCollection();
_applicationObjects.Add(objectBuilder.ID,
objectBuilder.ObjectType,
objectBuilder.LateBound);
}
else {
Debug.Assert(false, "Unexpected scope!");
}
}
/*
* Add a child builder to a builder
*/
private void AppendSubBuilder(ControlBuilder builder, ControlBuilder subBuilder) {
// Check if it's an object tag
if (subBuilder is ObjectTagBuilder) {
ProcessObjectTag((ObjectTagBuilder) subBuilder);
return;
}
builder.AppendSubBuilder(subBuilder);
}
/*
* Process an opening tag (possibly self-closed)
*/
// Used to generate unique id's
private int _controlCount;
private bool ProcessBeginTag(Match match, string inputText) {
string tagName = match.Groups["tagname"].Value;
// Get all the attributes into a bag
ParsedAttributeCollection attribs;
string duplicateAttribute;
ProcessAttributes(inputText, match, out attribs, false /*fDirective*/, out duplicateAttribute);
// Check if the tag is self closed
bool fSelfClosed = match.Groups["empty"].Success;
// Is it a server side script tag?
if (StringUtil.EqualsIgnoreCase(tagName, "script") && flags[isServerTag]) {
ProcessScriptTag(match, inputText, attribs, fSelfClosed);
return true;
}
// Process the settings that may come from config when the first non-trivial tag is parsed. VS Whidbey 141882
if (!flags[mainDirectiveHandled]) {
ProcessMainDirective(_mainDirectiveConfigSettings);
flags[mainDirectiveHandled] = true;
}
ControlBuilder parentBuilder = null;
ControlBuilder subBuilder = null;
Type childType = null;
// This could be a property of an object that is filterable
string realTagName;
string filter = Util.ParsePropertyDeviceFilter(tagName, out realTagName);
// Check if the parent builder wants to create a subcontrol for this tag.
if (BuilderStack.Count > 1) {
parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
// If the parent builder is a StringPropertyBuilder, we want to treat everything
// in it as a literal, so we always return false here (VSWhidbey 285429)
if (parentBuilder is StringPropertyBuilder)
return false;
subBuilder = parentBuilder.CreateChildBuilder(filter, realTagName, attribs,
this, parentBuilder, _id, _lineNumber, CurrentVirtualPath, ref childType, false);
}
// If not, use the root builder if runat=server is there.
if (subBuilder == null && flags[isServerTag]) {
subBuilder = RootBuilder.CreateChildBuilder(filter, realTagName, attribs,
this, parentBuilder, _id, _lineNumber, CurrentVirtualPath, ref childType, false);
}
// In case we find that the top stack item has the same name as the
// current tag, we increase a count on the stack item. This way, we
// know that we need to ignore the corresponding closing tag (ASURT 50795)
if (subBuilder == null && _builderStack.Count > 1 && !fSelfClosed) {
BuilderStackEntry stackEntry = (BuilderStackEntry) _builderStack.Peek();
if (StringUtil.EqualsIgnoreCase(tagName, stackEntry._tagName))
stackEntry._repeatCount++;
}
// We could not get the type of a server control from that tag
if (subBuilder == null) {
// If it wasn't marked as runat=server, ignore
if (!flags[isServerTag] || IgnoreParseErrors)
return false;
// If it was marked as runat=server, fail
ProcessError(SR.GetString(SR.Unknown_server_tag, tagName));
return true;
}
// We have a server control
// If we have a control type filter, make sure the child control is allowed
if (_pageParserFilter != null) {
Debug.Assert(childType != null);
if (!_pageParserFilter.AllowControlInternal(childType, subBuilder)) {
ProcessError(SR.GetString(SR.Control_type_not_allowed, childType.FullName));
return true;
}
}
// Make sure it doesn't have duplicated attributes
if (duplicateAttribute != null) {
ProcessError(SR.GetString(SR.Duplicate_attr_in_tag, duplicateAttribute));
}
// Get the id from the builder. Note that it may be null even if _id was not initially null,
// if the builder is not for a Control (VSWhidbey 406302)
_id = subBuilder.ID;
// If it has an id, enforce validity and uniqueness
if (_id != null) {
if (!System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(_id)) {
ProcessError(SR.GetString(SR.Invalid_identifier, _id));
return true;
}
if (_idList.Contains(_id)) {
ProcessError(SR.GetString(SR.Id_already_used, _id));
return true;
}
_idList.Add(_id);
}
else if (flags[isServerTag]) {
// Make sure that cached controls always have a fixed id to prevent
// unpredictable behavior (ASURT 83402)
PartialCachingAttribute cacheAttrib = (PartialCachingAttribute)
TypeDescriptor.GetAttributes(childType)[typeof(PartialCachingAttribute)];
// If we are parsing a theme file, the controls do not have an ID,
// and we should not be adding one. (Dev10 bug 660310)
if (!(subBuilder.Parser is PageThemeParser) && cacheAttrib != null) {
_id = "_ctrl_" + _controlCount.ToString(NumberFormatInfo.InvariantInfo);
subBuilder.ID = _id;
_controlCount++;
// Controls can't be filtered, so use the default filter
subBuilder.PreprocessAttribute(String.Empty, "id", _id, false /*mainDirectiveMode*/);
}
}
// Take care of the previous literal string
ProcessLiteral();
if (childType != null) {
// Update the hash code with the name of the control's type
UpdateTypeHashCode(childType.FullName);
}
// If the server control has a body, and if it didn't self-close
// (i.e. wasn't terminated with "/>"), put it on the stack of controls.
if (!fSelfClosed && subBuilder.HasBody()) {
// If it's a template, push a new ID list (ASURT 72773)
if (subBuilder is TemplateBuilder && ((TemplateBuilder)subBuilder).AllowMultipleInstances) {
_idListStack.Push(_idList);
_idList = new CaseInsensitiveStringSet();
}
_builderStack.Push(new BuilderStackEntry(subBuilder, tagName,
CurrentVirtualPathString, _lineNumber,
inputText, match.Index + match.Length));
// Optionally record begin tag position data
ParseRecorders.RecordBeginTag(subBuilder, match);
}
else {
// Append the sub builder to the current builder
parentBuilder = ((BuilderStackEntry) _builderStack.Peek())._builder;
AppendSubBuilder(parentBuilder, subBuilder);
// Tell the builder that we're done parsing its control
subBuilder.CloseControl();
// Optionally record empty tag position data
ParseRecorders.RecordEmptyTag(subBuilder, match);
}
return true;
}
/*
* Process a <script runat=server> tag
*/
private void ProcessScriptTag(Match match, string text, IDictionary attribs, bool fSelfClosed) {
ProcessLiteral();
// Always ignore the spaces after a script tag
flags[ignoreNextSpaceString] = true;
// Check if there is a 'src' attribute
VirtualPath virtualPath = Util.GetAndRemoveVirtualPathAttribute(attribs, "src");
if (virtualPath != null) {
// Make sure it's legal to have code in this page
EnsureCodeAllowed();
// Get a full path to the script file
virtualPath = ResolveVirtualPath(virtualPath);
// Make sure access to the file is allowed (VSWhidbey 195545)
HttpRuntime.CheckVirtualFilePermission(virtualPath.VirtualPathString);
AddSourceDependency(virtualPath);
ProcessLanguageAttribute((string)attribs["language"]);
_currentScript = new ScriptBlockData(1, 1, virtualPath.VirtualPathString);
_currentScript.Script = Util.StringFromVirtualPath(virtualPath);
// Add this script to the script builder
_scriptList.Add(_currentScript);
_currentScript = null;
// If the script tag is not self closed (even though it has a
// src attribute), continue processing it, but eventually
// ignore the content (ASURT 8883)
if (!fSelfClosed) {
flags[inScriptTag] = true;
_scriptStartLineNumber = _lineNumber;
flags[ignoreScriptTag] = true;
}
return;
}
ProcessLanguageAttribute((string)attribs["language"]);
// Look for the last newline before the script block code string
int startOfCode = match.Index + match.Length;
int newlineIndex = text.LastIndexOfAny(s_newlineChars, startOfCode-1);
// Use it to calculate the column where the code starts,
// which improves the debugging experience (VSWhidbey 87172)
int column = startOfCode-newlineIndex;
Debug.Assert(column > 0);
_currentScript = new ScriptBlockData(_lineNumber, column, CurrentVirtualPathString);
// No 'src' attribute. Make sure tag is not self closed.
if (fSelfClosed) {
ProcessError(SR.GetString(SR.Script_tag_without_src_must_have_content));
}
flags[inScriptTag] = true;
_scriptStartLineNumber = _lineNumber;
}
/*
* Called when a '</' sequence is seen. This means we can start closing
* tags.
*/
private bool ProcessEndTag(Match match) {
string tagName = match.Groups["tagname"].Value;
// If we are in the middle of a server side SCRIPT tag
if (flags[inScriptTag]) {
// Ignore anything that's not a </script>
if (!StringUtil.EqualsIgnoreCase(tagName, "script"))
return false;
ProcessServerScript();
flags[inScriptTag] = false;
flags[ignoreScriptTag] = false;
return true;
}
// See if anyone on the stack cares about termination.
return MaybeTerminateControl(tagName, match);
}
internal bool IsExpressionBuilderValue(string val) {
return ControlBuilder.expressionBuilderRegex.Match(val, 0).Success;
}
internal abstract string DefaultDirectiveName { get; }
// If there is a parser filter, give it a chance to look at the directive
internal void PreprocessDirective(string directiveName, IDictionary directive) {
// No parser filter: done
if (_pageParserFilter == null)
return;
if (directiveName.Length == 0)
directiveName = DefaultDirectiveName;
_pageParserFilter.PreprocessDirective(directiveName, directive);
}
/*
* Process a <%@ %> block
*/
internal virtual void ProcessDirective(string directiveName, IDictionary directive) {
// Check for the main directive, which is "page" for an aspx,
// and "application" for global.asax
if (directiveName.Length == 0) {
if (FInDesigner) {
return;
}
// Make sure the main directive was not already specified
if (flags[mainDirectiveSpecified]) {
ProcessError(SR.GetString(SR.Only_one_directive_allowed, DefaultDirectiveName));
return;
}
// If there are some config settings that were not overriden, add them to the list
if (_mainDirectiveConfigSettings != null) {
// Go through all the config attributes
foreach (DictionaryEntry entry in _mainDirectiveConfigSettings) {
// If it was overridden, ignore the config setting
if (directive.Contains(entry.Key))
continue;
// Add it to the list
directive[entry.Key] = entry.Value;
}
}
ProcessMainDirective(directive);
flags[mainDirectiveSpecified] = true;
flags[mainDirectiveHandled] = true;
}
else if (StringUtil.EqualsIgnoreCase(directiveName, "assembly")) {
// Assembly directive
// Even though this only makes sense for compiled pages, Sharepoint needs us to
// ignore instead of throw when the page in non-compiled.
// Remove the attributes as we get them from the dictionary
string assemblyName = Util.GetAndRemoveNonEmptyAttribute(directive, "name");
VirtualPath src = Util.GetAndRemoveVirtualPathAttribute(directive, "src");
// If there are some attributes left, fail
Util.CheckUnknownDirectiveAttributes(directiveName, directive);
if (assemblyName != null && src != null) {
ProcessError(SR.GetString(SR.Attributes_mutually_exclusive, "Name", "Src"));
}
if (assemblyName != null) {
AddAssemblyDependency(assemblyName);
}
// Is it a source file that needs to be compiled on the fly
else if (src != null) {
ImportSourceFile(src);
}
else {
ProcessError(SR.GetString(SR.Missing_attr, "name"));
}
}
else if (StringUtil.EqualsIgnoreCase(directiveName, "import")) {
// Import directive
ProcessImportDirective(directiveName, directive);
}
else if (StringUtil.EqualsIgnoreCase(directiveName, "implements")) {
// 'implements' directive
// We must compile the page if it asks to implement an interface
OnFoundDirectiveRequiringCompilation(directiveName);
// Remove the attributes as we get them from the dictionary
string interfaceName = Util.GetAndRemoveRequiredAttribute(directive, "interface");
// If there are some attributes left, fail
Util.CheckUnknownDirectiveAttributes(directiveName, directive);
Type interfaceType = GetType(interfaceName);
// Make sure that it's an interface
if (!interfaceType.IsInterface) {
ProcessError(SR.GetString(SR.Invalid_type_to_implement, interfaceName));
return;
}
// Add the interface type to the list
if (_implementedInterfaces == null) {
_implementedInterfaces = new ArrayList();
}
_implementedInterfaces.Add(interfaceType);
}
else if (!FInDesigner) {
ProcessError(SR.GetString(SR.Unknown_directive, directiveName));
}
}
internal virtual void ProcessMainDirective(IDictionary mainDirective) {
// Used to store some temporary data resulting from the parsing of the directive
IDictionary parseData = new HybridDictionary();
// Keep track of unknown attributes
ParsedAttributeCollection unknownAttributes = null;
// Go through all the attributes on the directive
foreach (DictionaryEntry entry in mainDirective) {
string attribName = (string)entry.Key;
// Parse out the device name, if any
string deviceName = Util.ParsePropertyDeviceFilter(attribName, out attribName);
try {
// Try to process the attribute, and if not, keep track of it in the 'unknown' list
if (!ProcessMainDirectiveAttribute(deviceName, attribName, (string)entry.Value, parseData)) {
if (unknownAttributes == null) {
unknownAttributes = CreateEmptyAttributeBag();
}
unknownAttributes.AddFilteredAttribute(deviceName, attribName, (string)entry.Value);
}
}
catch (Exception e) {
ProcessException(e);
}
}
// Allow some postprocessing to happen, in case attributes have dependencies
// on each other (e.g. mutually exclusive attributes).
PostProcessMainDirectiveAttributes(parseData);
// We should always set the control type of the root builder regardless of unknown attributes
RootBuilder.SetControlType(BaseType);
// If we didn't have any unknown attributes, we're done
if (unknownAttributes == null)
return;
RootBuilder.ProcessImplicitResources(unknownAttributes);
// Process all the unknown attributes
foreach (FilteredAttributeDictionary filteredAttributes in unknownAttributes.GetFilteredAttributeDictionaries()) {
string filter = filteredAttributes.Filter;
foreach (DictionaryEntry attribute in filteredAttributes) {
string attribName = (string)attribute.Key;
ProcessUnknownMainDirectiveAttribute(filter, attribName, (string) attribute.Value);
}
}
}
internal virtual bool ProcessMainDirectiveAttribute(string deviceName, string name,
string value, IDictionary parseData) {
switch (name) {
// Ignore description and codebehind attributes
case "description":
case "codebehind":
break;
case "debug":
flags[debug] = Util.GetBooleanAttribute(name, value);
if (flags[debug] && !HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "debug"));
}
flags[hasDebugAttribute] = true;
break;
case "linepragmas":
flags[noLinePragmas] = !Util.GetBooleanAttribute(name, value);
break;
case "warninglevel":
_warningLevel = Util.GetNonNegativeIntegerAttribute(name, value);
break;
case "compileroptions":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
string compilerOptions = value.Trim();
CompilationUtil.CheckCompilerOptionsAllowed(compilerOptions, false /*config*/, null, 0);
_compilerOptions = compilerOptions;
break;
// These two really only make sense in VB
case "explicit":
flags[useExplicit] = Util.GetBooleanAttribute(name, value);
break;
case "strict":
flags[strict] = Util.GetBooleanAttribute(name, value);
break;
case "language":
ValidateBuiltInAttribute(deviceName, name, value);
string language = Util.GetNonEmptyAttribute(name, value);
ProcessLanguageAttribute(language);
break;
// A "src" attribute is equivalent to an imported source file
case "src":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
// Remember the src assembly for post processing
parseData[name] = Util.GetNonEmptyAttribute(name, value);
break;
case "inherits":
// Remember the base class for post processing
parseData[name] = Util.GetNonEmptyAttribute(name, value);
break;
case "classname":
// Even though this only makes sense for compiled pages, Sharepoint needs us to
// ignore instead of throw when the page in non-compiled.
_generatedClassName = Util.GetNonEmptyFullClassNameAttribute(name, value,
ref _generatedNamespace);
break;
case "codefile":
// This only makes sense for compiled pages
OnFoundAttributeRequiringCompilation(name);
try {
ProcessCodeFile(VirtualPath.Create(Util.GetNonEmptyAttribute(name, value)));
}
catch (Exception ex) {
ProcessException(ex);
}
break;
default:
// We didn't handle the attribute
return false;
}
// The attribute was handled
// Make sure no device filter or resource expression was specified
ValidateBuiltInAttribute(deviceName, name, value);
return true;
}
// Throw an exception if there is a device filter or resource expression
internal void ValidateBuiltInAttribute(string deviceName, string name, string value) {
Debug.Assert(deviceName != null);
if (IsExpressionBuilderValue(value)) {
ProcessError(SR.GetString(SR.Illegal_Resource_Builder, name));
}
if (deviceName.Length > 0) {
ProcessError(SR.GetString(SR.Illegal_Device, name));
}
}
internal virtual void ProcessUnknownMainDirectiveAttribute(string filter, string attribName, string value) {
// By default, it is not legal to have unknown attributes. But derived parser
// classes can change this behavior
ProcessError(SR.GetString(SR.Attr_not_supported_in_directive,
attribName, DefaultDirectiveName));
}
internal virtual void PostProcessMainDirectiveAttributes(IDictionary parseData) {
// Post process the src and inherits attributes
string src = (string) parseData["src"];
Assembly assembly = null;
if (src != null) {
try {
assembly = ImportSourceFile(VirtualPath.Create(src));
}
catch (Exception ex) {
ProcessException(ex);
}
}
// Was a code file base type specified in the directive
string codeFileBaseTypeName = (string)parseData[CodeFileBaseClassAttributeName];
// If so, there must also be a CodeFile attribute
if (codeFileBaseTypeName != null && _codeFileVirtualPath == null) {
throw new HttpException(SR.GetString(SR.CodeFileBaseClass_Without_Codefile));
}
// Was a base type specified in the directive
string baseTypeName = (string) parseData["inherits"];
if (baseTypeName != null) {
try {
ProcessInheritsAttribute(baseTypeName, codeFileBaseTypeName, src, assembly);
}
catch (Exception ex) {
ProcessException(ex);
}
}
else {
if (_codeFileVirtualPath != null) {
throw new HttpException(SR.GetString(SR.Codefile_without_inherits));
}
}
}
private void ProcessInheritsAttribute(string baseTypeName, string codeFileBaseTypeName,
string src, Assembly assembly) {
// If a code file is used, then the inherits attribute points to the class in that file,
// which is not yet compiled. In that case, we cannot get the Type, so we just keep
// track of the class name (and namespace)
if (_codeFileVirtualPath != null) {
_baseTypeName = Util.GetNonEmptyFullClassNameAttribute("inherits", baseTypeName,
ref _baseTypeNamespace);
// Now set baseTypeName to codeFileBaseTypeName, so that if it was set, it will
// be used as the BaseType during parsing (DevDiv 43024)
baseTypeName = codeFileBaseTypeName;
if (baseTypeName == null)
return;
}
Type baseType = null;
if (assembly != null)
baseType = assembly.GetType(baseTypeName, false /* throwOnError */, true /* caseInsensitive */);
else {
try {
baseType = GetType(baseTypeName);
}
catch {
// We couldn't load the inherits type. If the classname attribute has a
// namespace, check if the inherits type exists in there (VSWhidbey 297056)
// If the classname attribute doesn't have a namespace, give up
if (_generatedNamespace == null)
throw;
// If the inherits attribute already had a namespace, don't try this
if (baseTypeName.IndexOf('.') >= 0)
throw;
try {
// Try loading the inherit using the classname's namespace
string baseTypeNameWithNS = _generatedNamespace + "." + baseTypeName;
baseType = GetType(baseTypeNameWithNS);
}
catch {}
// If that failed too, rethrow the original exception
if (baseType == null)
throw;
}
}
// Make sure we successfully got the Type of the base class
if (baseType == null) {
Debug.Assert(assembly != null, "assembly != null");
ProcessError(SR.GetString(SR.Non_existent_base_type, baseTypeName, src));
return;
}
// Make sure the base type extends the DefaultBaseType (Page or UserControl)
if (!DefaultBaseType.IsAssignableFrom(baseType)) {
ProcessError(SR.GetString(SR.Invalid_type_to_inherit_from, baseTypeName,
_baseType.FullName));
return;
}
// If we have a control type filter, make sure the base type is allowed
if (_pageParserFilter != null) {
if (!_pageParserFilter.AllowBaseType(baseType)) {
throw new HttpException(
SR.GetString(SR.Base_type_not_allowed, baseType.FullName));
}
}
_baseType = baseType;
// Now that we have the base type, we can create the RootBuilder
Debug.Assert(_rootBuilder == null);
EnsureRootBuilderCreated();
// Make sure we link with the assembly of the base type (ASURT 101778)
AddTypeDependency(_baseType);
// Remember the fact that the page uses codebehind
flags[hasCodeBehind] = true;
}
private void ProcessImportDirective(string directiveName, IDictionary directive) {
// Even though this only makes sense for compiled pages, Sharepoint needs us to
// ignore instead of throw when the page in non-compiled.
// Remove the attributes as we get them from the dictionary
string ns = Util.GetAndRemoveNonEmptyNoSpaceAttribute(directive, "namespace");
if (ns == null)
ProcessError(SR.GetString(SR.Missing_attr, "namespace"));
else
AddImportEntry(ns);
// If there are some attributes left, fail
Util.CheckUnknownDirectiveAttributes(directiveName, directive);
}
/*
* Process a language attribute, as can appear in the Page directive and in
* <script runat=server> tags.
*/
private void ProcessLanguageAttribute(string language) {
if (language == null)
return;
// We don't have CompilationConfig at design-time and the language attribute isn't used either.
if (FInDesigner)
return;
CompilerType compilerType = CompilationUtil.GetCompilerInfoFromLanguage(
CurrentVirtualPath, language);
// Make sure we don't get conflicting languages
if (_compilerType != null &&
_compilerType.CodeDomProviderType != compilerType.CodeDomProviderType) {
ProcessError(SR.GetString(SR.Mixed_lang_not_supported, language));
return;
}
_compilerType = compilerType;
}
/*
* Process a compileFile attribute (aka code besides)
*/
private void ProcessCodeFile(VirtualPath codeFileVirtualPath) {
Debug.Assert(_codeFileVirtualPath == null);
_codeFileVirtualPath = ResolveVirtualPath(codeFileVirtualPath);
// Get the language for the code beside page
CompilerType compilerType = CompilationUtil.GetCompilerInfoFromVirtualPath(
_codeFileVirtualPath);
// Make sure we don't get conflicting languages
if (_compilerType != null &&
_compilerType.CodeDomProviderType != compilerType.CodeDomProviderType) {
ProcessError(SR.GetString(SR.Inconsistent_CodeFile_Language));
return;
}
// Check if it's trying to go cross app, or points to a special directory.
// It's important to do this before checkin existence, to avoid revealing information
// about other apps (VSWhidbey 442957)
BuildManager.ValidateCodeFileVirtualPath(_codeFileVirtualPath);
// Make sure the file exists
Util.CheckVirtualFileExists(_codeFileVirtualPath);
_compilerType = compilerType;
// Add the code file to the list of files we depend on
AddSourceDependency(_codeFileVirtualPath);
}
/*
* Compile a source file into an assembly, and import it
*/
private Assembly ImportSourceFile(VirtualPath virtualPath) {
// If it's a no-compile page, ignore the imported source file
if (CompilationMode == CompilationMode.Never)
return null;
// Get a full path to the source file
virtualPath = ResolveVirtualPath(virtualPath);
// If we have a page parser filter, make sure the reference is allowed
if (_pageParserFilter != null && !_pageParserFilter.AllowVirtualReference(CompConfig, virtualPath)) {
ProcessError(SR.GetString(SR.Reference_not_allowed, virtualPath));
}
// Add the source file to the list of files we depend on
AddSourceDependency(virtualPath);
// Compile it into an assembly
BuildResultCompiledAssembly result = BuildManager.GetVPathBuildResult(
virtualPath) as BuildResultCompiledAssembly;
if (result == null) {
ProcessError(SR.GetString(SR.Not_a_src_file, virtualPath));
}
Assembly a = result.ResultAssembly;
// Add a dependency to the assembly and its dependencies
AddAssemblyDependency(a, true /*addDependentAssemblies*/);
return a;
}
/*
* If we could not match the '<' at all, check for some specific syntax
* errors.
*/
private void DetectSpecialServerTagError(string text, int textPos) {
if (IgnoreParseErrors) return;
// If it started with <%, it's probably not closed (ASURT 13661)
if (text.Length > textPos+1 && text[textPos+1] == '%') {
ProcessError(SR.GetString(SR.Malformed_server_block));
return;
}
// Search for the end of the tag ('>')
Match match = gtRegex.Match(text, textPos);
// No match, return
if (!match.Success)
return;
// Get the complete potential tag
string tag = text.Substring(textPos, match.Index-textPos+2);
// Check if it's a case of nested <% %> block in a server tag (ASURT 8714)
// If the tag does not contain runat=server, do nothing
match = runatServerRegex.Match(tag);
if (!match.Success)
return;
// If it has runat=server, but there is a '<' before it, don't fail, since
// this '<' is probably the true tag start, and it will be processed later (ASURT 39531)
// But ignore "<%" (ASURT 77554)
Match matchLessThan = ltRegex.Match(tag, 1);
if (matchLessThan.Success && matchLessThan.Index < match.Index)
return;
System.Web.Util.Debug.Trace("Template", "Found malformed server tag: " + tag);
// Remove all <% %> constructs from within it.
string tag2 = serverTagsRegex.Replace(tag, String.Empty);
// If there were some <% %> constructs in the tag
if ((object)tag2 != (object)tag) {
// If it can be parsed as a tag after we removed the <% %> constructs, fail
if (TagRegex.Match(tag2).Success) {
ProcessError(SR.GetString(SR.Server_tags_cant_contain_percent_constructs));
return;
}
}
// Give a more generic error (fixed 18969, 30312)
ProcessError(SR.GetString(SR.Malformed_server_tag));
}
/*
* Add an entry to our list of NamespaceEntry's
*/
internal void AddImportEntry(string ns) {
// We're about to modify the list of namespaces, so if we already
// have one (coming from config), clone it so we don't modify theirs.
if (_namespaceEntries != null)
_namespaceEntries = (Hashtable) _namespaceEntries.Clone();
else
_namespaceEntries = new Hashtable();
NamespaceEntry namespaceEntry = new NamespaceEntry();
namespaceEntry.Namespace = ns;
namespaceEntry.Line = _lineNumber;
namespaceEntry.VirtualPath = CurrentVirtualPathString;
_namespaceEntries[ns] = namespaceEntry;
}
internal Assembly LoadAssembly(string assemblyName, bool throwOnFail) {
if (_typeResolutionService != null) {
AssemblyName asmName = new AssemblyName(assemblyName);
return _typeResolutionService.GetAssembly(asmName, throwOnFail);
}
return _compConfig.LoadAssembly(assemblyName, throwOnFail);
}
internal Type GetType(string typeName, bool ignoreCase) {
return GetType(typeName, ignoreCase, /* throwOnError */ true);
}
/*
* Look for a type by name in the assemblies that this page links with
*/
internal Type GetType(string typeName, bool ignoreCase, bool throwOnError) {
// If it contains an assembly name, parse it out and load the assembly (ASURT 53589)
Assembly a = null;
int commaIndex = Util.CommaIndexInTypeName(typeName);
if (commaIndex > 0) {
string assemblyName = typeName.Substring(commaIndex + 1).Trim();
typeName = typeName.Substring(0, commaIndex).Trim();
try {
a = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
}
catch {
throw new HttpException(
SR.GetString(SR.Assembly_not_compiled, assemblyName));
}
}
// If we got an assembly, load the type from it
if (a != null)
return a.GetType(typeName, throwOnError, ignoreCase);
// Otherwise, look for the type in the referenced assemblies (given by the build system)
Type t;
t = Util.GetTypeFromAssemblies(_referencedAssemblies, typeName, ignoreCase);
if (t != null)
return t;
// Or in the assemblies that this page depends on
t = Util.GetTypeFromAssemblies(AssemblyDependencies, typeName, ignoreCase);
if (t != null)
return t;
if (throwOnError) {
throw new HttpException(
SR.GetString(SR.Invalid_type, typeName));
}
return null;
}
/*
* Look for a type by name in the assemblies that this page links with
*/
internal Type GetType(string typeName) {
return GetType(typeName, false /*ignoreCase*/);
}
/*
* Process a server side include. e.g. <!-- #include file="foo.inc" -->
*/
private void ProcessServerInclude(Match match) {
if (flags[inScriptTag]) {
throw new HttpException(
SR.GetString(SR.Include_not_allowed_in_server_script_tag));
}
ProcessLiteral();
string pathType = match.Groups["pathtype"].Value;
string filename = match.Groups["filename"].Value;
//System.Web.Util.Debug.Trace("Template", "#Include " + pathType + "=" + filename);
if (filename.Length == 0) {
ProcessError(SR.GetString(SR.Empty_file_name));
return;
}
VirtualPath newVirtualPath = CurrentVirtualPath;
string newPhysicalPath = null;
if (StringUtil.EqualsIgnoreCase(pathType, "file")) {
if (UrlPath.IsAbsolutePhysicalPath(filename)) {
// If it's an absolute physical path, use it as is
newPhysicalPath = filename;
}
else {
// If it's relative, try to treat it as virtual
bool treatAsVirtual = true;
try {
newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
}
catch {
// If this fails, it probably means it tried to escape the root of the app.
// In that case, fall back to treating it as physical (VSWhidbey 339705)
treatAsVirtual = false;
}
if (treatAsVirtual) {
HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
AddSourceDependency(newVirtualPath);
}
else {
// Treat it as relative to the physical path of the current page
string currentPhysicalDir = Path.GetDirectoryName(
CurrentVirtualPath.MapPath());
newPhysicalPath = Path.GetFullPath(Path.Combine(currentPhysicalDir, filename.Replace('/', '\\')));
}
}
}
else if (StringUtil.EqualsIgnoreCase(pathType, "virtual")) {
newVirtualPath = ResolveVirtualPath(VirtualPath.Create(filename));
HttpRuntime.CheckVirtualFilePermission(newVirtualPath.VirtualPathString);
AddSourceDependency(newVirtualPath);
}
else {
ProcessError(SR.GetString(SR.Only_file_virtual_supported_on_server_include));
return;
}
if (newPhysicalPath != null) {
// Make sure that access to the file is permitted (ASURT 73792,85467)
HttpRuntime.CheckFilePermission(newPhysicalPath);
}
// If there is a filter, check whether it allows this include file
if (_pageParserFilter != null && !_pageParserFilter.AllowServerSideInclude(newVirtualPath.VirtualPathString)) {
ProcessError(SR.GetString(SR.Include_not_allowed, newVirtualPath));
}
// Parse the included file recursively
ParseFile(newPhysicalPath, newVirtualPath);
// Always ignore the spaces after an include directive
flags[ignoreNextSpaceString] = true;
}
private static char[] s_newlineChars = new char[] { '\r', '\n' };
/*
* Handle <%= ... %>, <%# ... %> and <% ... %> blocks
*/
private void ProcessCodeBlock(Match match, CodeBlockType blockType, string text) {
// Take care of the previous literal string
ProcessLiteral();
// Get the piece of code
Group codeGroup = match.Groups["code"];
string code = codeGroup.Value;
bool encode = match.Groups["encode"].Success;
// Replace "%\>" with "%>" (ASURT 7175)
code = code.Replace(@"%\>", "%>");
int lineNumber = _lineNumber;
int column = -1;
if (blockType != CodeBlockType.Code) {
// It a <%= %>, <%# %> or <%: %> block. We need to do special handling of newline chars
// If there are newlines in the beginning of the code string we need to get rid of them,
// and adjust the line pragma accordingly. This is needed because some compilers (like VB)
// don't support multiline expression (ASURT 13662)
int newlineIndex = -1;
for (int i = 0; i < code.Length && Char.IsWhiteSpace(code[i]); i++) {
if (code[i] == '\r' || (code[i] == '\n' && (i == 0 || code[i - 1] != '\r'))) {
lineNumber++;
newlineIndex = i;
}
else if (code[i] == '\n') {
newlineIndex = i;
}
}
if (newlineIndex >= 0) {
// If we found some newlines in the beginning, get rid of them. Note
// that we preserve the spaces, to get correct column information
code = code.Substring(newlineIndex + 1);
// The code starts at column 1 (since we keep the spaces)
column = 1;
}
// Same deal for the end of the string: look for the first newline
// after the last non-blank chararacter
newlineIndex = -1;
for (int i = code.Length - 1; i >= 0 && Char.IsWhiteSpace(code[i]); i--) {
if (code[i] == '\r' || code[i] == '\n')
newlineIndex = i;
}
// And if we found one, remove it and everything after
if (newlineIndex >= 0)
code = code.Substring(0, newlineIndex);
// Disallow empty expressions (ASURT 40124)
// Do not treat as error in CBM. This is necessary so we still generate
// code blocks for empty expressions. (VSWhidbey 406212)
if (!IgnoreParseErrors && Util.IsWhiteSpaceString(code)) {
ProcessError(SR.GetString(SR.Empty_expression));
return;
}
}
if (column < 0) {
// It a <% %> block. Newline chars are not a problem.
// Look for the last newline before the code string
int newlineIndex = text.LastIndexOfAny(s_newlineChars, codeGroup.Index-1);
// Use it to calculate the column where the code starts,
// which improves the debugging experience (VSWhidbey 87172)
column = codeGroup.Index-newlineIndex;
Debug.Assert(column > 0);
}
ControlBuilder builder = ((BuilderStackEntry) BuilderStack.Peek())._builder;
ControlBuilder subBuilder;
// First, give the PageParserFilter a chance to handle the code block
if (!PageParserFilterProcessedCodeBlock(CodeConstructTypeFromCodeBlockType(blockType), code, lineNumber)) {
// Make sure it's legal to have code in this page
EnsureCodeAllowed();
// Add the code block to the top builder
subBuilder = new CodeBlockBuilder(blockType, code, lineNumber, column, CurrentVirtualPath, encode);
AppendSubBuilder(builder, subBuilder);
// Optionally record code block position data
ParseRecorders.RecordCodeBlock(subBuilder, match);
}
// Always ignore the spaces after a <% ... %> block
if (blockType == CodeBlockType.Code)
flags[ignoreNextSpaceString] = true;
}
// Map a CodeBlockType to the equivalent CodeConstructType
private static CodeConstructType CodeConstructTypeFromCodeBlockType(CodeBlockType blockType) {
switch (blockType) {
case CodeBlockType.Code:
return CodeConstructType.CodeSnippet;
case CodeBlockType.Expression:
return CodeConstructType.ExpressionSnippet;
case CodeBlockType.EncodedExpression:
return CodeConstructType.EncodedExpressionSnippet;
case CodeBlockType.DataBinding:
return CodeConstructType.DataBindingSnippet;
default:
Debug.Assert(false);
return CodeConstructType.CodeSnippet;
}
}
private bool PageParserFilterProcessedCodeBlock(CodeConstructType codeConstructType,
string code, int lineNumber) {
// This requires a PageParserFilter, and CompilationMode must allow it
if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
return false;
// Temporarily adjust the line number to match what's passed in. This makes it correctly
// point to the begining of <script> blocks rather than the end
int restoreLineNumber = _lineNumber;
_lineNumber = lineNumber;
try {
// Ask the PageParserFilter whether it wants to process it
return _pageParserFilter.ProcessCodeConstruct(codeConstructType, code);
}
finally {
_lineNumber = restoreLineNumber;
}
}
internal bool PageParserFilterProcessedDataBindingAttribute(string controlId, string attributeName,
string code) {
// This requires a PageParserFilter, and CompilationMode must allow it
if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
return false;
// Ask the PageParserFilter whether it wants to process it
return _pageParserFilter.ProcessDataBindingAttribute(controlId, attributeName, code);
}
internal bool PageParserFilterProcessedEventHookupAttribute(string controlId, string eventName,
string handlerName) {
// This requires a PageParserFilter, and CompilationMode must allow it
if (_pageParserFilter == null || CompilationMode == CompilationMode.Never)
return false;
// Ask the PageParserFilter whether it wants to process it
return _pageParserFilter.ProcessEventHookup(controlId, eventName, handlerName);
}
// Add a ControlBuilder in the tree at the current parser position
internal void AddControl(Type type, IDictionary attributes) {
ControlBuilder parentBuilder = ((BuilderStackEntry)BuilderStack.Peek())._builder;
ControlBuilder subBuilder = ControlBuilder.CreateBuilderFromType(this, parentBuilder,
type, null, null, attributes, _lineNumber,
CurrentVirtualPath.VirtualPathString);
AppendSubBuilder(parentBuilder, subBuilder);
}
/*
* Adds attributes and their values to the attribs
* Sets the _id and isServerTag data members as appropriate.
* If fDirective is true, we are being called for a <%@ %> block, in
* which case the name of the directive is returned (e.g. "page")
*/
private string ProcessAttributes(string text, Match match, out ParsedAttributeCollection attribs,
bool fDirective, out string duplicateAttribute) {
string directiveName = String.Empty;
attribs = CreateEmptyAttributeBag();;
CaptureCollection attrnames = match.Groups["attrname"].Captures;
CaptureCollection attrvalues = match.Groups["attrval"].Captures;
CaptureCollection equalsign = null;
if (fDirective)
equalsign = match.Groups["equal"].Captures;
flags[isServerTag] = false;
_id = null;
duplicateAttribute = null;
for (int i = 0; i < attrnames.Count; i++) {
string attribName = attrnames[i].ToString();
// If processing a directive, set the attribute name to lower case for easier processing later
if (fDirective)
attribName = attribName.ToLower(CultureInfo.InvariantCulture);
Capture attrValue = attrvalues[i];
string attribValue = attrValue.ToString();
// Any of the attributes could be filtered
string realAttributeName = String.Empty;
string filter = Util.ParsePropertyDeviceFilter(attribName, out realAttributeName);
// Always HTML decode all attributes (ASURT 54544)
attribValue = HttpUtility.HtmlDecode(attribValue);
// If we're parsing a directive, check if there is an equal sign.
bool fHasEqual = false;
if (fDirective)
fHasEqual = (equalsign[i].ToString().Length > 0);
// If this is a server ID, remember it
//
if (StringUtil.EqualsIgnoreCase(realAttributeName, "id")) {
_id = attribValue;
}
else if (StringUtil.EqualsIgnoreCase(realAttributeName, "runat")) {
// Make sure no device filter or resource expression was specified (VSWhidbey 85325)
ValidateBuiltInAttribute(filter, realAttributeName, attribValue);
// Only runat=server is valid
if (!StringUtil.EqualsIgnoreCase(attribValue, "server")) {
ProcessError(SR.GetString(SR.Runat_can_only_be_server));
}
// Set a flag if we see runat=server
flags[isServerTag] = true;
attribName = null; // Don't put it in attribute bag
}
else if (FInDesigner && StringUtil.EqualsIgnoreCase(realAttributeName, "ignoreParentFrozen")) {
// VSWhidbey 537398: "ignoreParentFrozen" is a special expando used in Venus. Ideally
// Venus would hide the expando altogether but that's not practical in Whidbey.
attribName = null;
}
if (attribName != null) {
// A <%@ %> block can have two formats:
// <%@ directive foo=1 bar=hello %>
// <%@ foo=1 bar=hello %>
// Check if we have the first format
if (fDirective && !fHasEqual && i==0) {
directiveName = attribName;
if (string.Compare(directiveName, DefaultDirectiveName,
StringComparison.OrdinalIgnoreCase) == 0) {
directiveName = String.Empty;
}
continue;
}
try {
// Don't allow filters in directive other than the main one
if (fDirective && directiveName.Length > 0 && filter.Length > 0) {
ProcessError(SR.GetString(SR.Device_unsupported_in_directive, directiveName));
continue;
}
attribs.AddFilteredAttribute(filter, realAttributeName, attribValue);
//Since the attribute column values are only used for generating line pragmas at design time for intellisense to work,
//we populate that only at design time so that runtime memory usage is not affected.
if (BuildManagerHost.InClientBuildManager) {
//Linenumber = Linenumber of tag beginning + new lines between tag beginning and attribute value beginning.
//Column = number of characters from the last new line (before the attrValue in the entire Text) till attrValue.
int lineNumber = _lineNumber + Util.LineCount(text, match.Index, attrValue.Index);
int column = attrValue.Index - text.LastIndexOfAny(s_newlineChars, attrValue.Index - 1);
attribs.AddAttributeValuePositionInformation(realAttributeName, lineNumber, column);
}
}
catch (ArgumentException) {
// Duplicate attribute. We can't throw until we find out if
// it's a server side tag (ASURT 51273)
duplicateAttribute = attribName;
}
catch (Exception ex) {
ProcessException(ex);
}
}
}
if (duplicateAttribute != null && fDirective) {
ProcessError(SR.GetString(SR.Duplicate_attr_in_directive, duplicateAttribute));
}
return directiveName;
}
private static ParsedAttributeCollection CreateEmptyAttributeBag() {
// use a ParsedAttributeCollection to preserve the order and store filtered information
return new ParsedAttributeCollection();
}
private bool MaybeTerminateControl(string tagName, Match match) {
BuilderStackEntry stackEntry = (BuilderStackEntry) BuilderStack.Peek();
ControlBuilder builder = stackEntry._builder;
// If the tag doesn't match, return false
if (stackEntry._tagName == null || !StringUtil.EqualsIgnoreCase(stackEntry._tagName, tagName)) {
return false;
}
// If the repeat count is non-zero, just decrease it
if (stackEntry._repeatCount > 0) {
stackEntry._repeatCount--;
return false;
}
// Take care of the previous literal string
ProcessLiteral();
// If the builder wants the raw text of the tag, give it to it
if (builder.NeedsTagInnerText()) {
try {
builder.SetTagInnerText(stackEntry._inputText.Substring(
stackEntry._textPos,
match.Index - stackEntry._textPos));
}
catch (Exception e) {
if (!IgnoreParseErrors) {
// Reset the line number to the beginning of the tag if there is an error
_lineNumber = builder.Line;
ProcessException(e);
return true;
}
}
}
// If it's ending a template, pop the idList (ASURT 72773)
if (builder is TemplateBuilder && ((TemplateBuilder)builder).AllowMultipleInstances)
_idList = (StringSet) _idListStack.Pop();
// Pop the top entry from the stack
_builderStack.Pop();
// Give the builder to its parent
AppendSubBuilder(((BuilderStackEntry) _builderStack.Peek())._builder, builder);
// Tell the builder that we're done parsing its control
builder.CloseControl();
// Optionally record end tag position data
ParseRecorders.RecordEndTag(builder, match);
return true;
}
/*
* Map a type name to a Type.
*/
internal Type MapStringToType(string typeName, IDictionary attribs) {
return RootBuilder.GetChildControlType(typeName, attribs);
}
/*
* Add a file as a dependency of the file we're parsing
*/
internal void AddSourceDependency(VirtualPath fileName) {
// Tell the filter that a dependency was added
if (_pageParserFilter != null) {
_pageParserFilter.OnDependencyAdded();
_pageParserFilter.OnDirectDependencyAdded();
}
AddSourceDependency2(fileName);
}
/*
* Add a file as a dependency of the file we're parsing
*/
private void AddSourceDependency2(VirtualPath fileName) {
if (_sourceDependencies == null)
_sourceDependencies = new CaseInsensitiveStringSet();
_sourceDependencies.Add(fileName.VirtualPathString);
}
/*
* Add a BuildResult's source dependencies to our own source dependencies
*/
internal void AddBuildResultDependency(BuildResult result) {
// Add one direct dependency
if (_pageParserFilter != null)
_pageParserFilter.OnDirectDependencyAdded();
if (result.VirtualPathDependencies == null)
return;
foreach (string virtualPath in result.VirtualPathDependencies) {
// Add one dependency for each file (to include direct and indirect)
if (_pageParserFilter != null)
_pageParserFilter.OnDependencyAdded();
AddSourceDependency2(VirtualPath.Create(virtualPath));
}
}
/*
* Add a type that we must 'link' with in order to build
*/
internal void AddTypeDependency(Type type) {
// We must link with all the types in the inheritance hierarchy (ASURT 83509)
AddBaseTypeDependencies(type);
// Add an import for the namespace of the type (if any)
// Per ASURT 83942, only do this for namespaces we generate (e.g. ASP & _ASP)
if (type.Namespace != null && BaseCodeDomTreeGenerator.IsAspNetNamespace(type.Namespace))
AddImportEntry(type.Namespace);
}
/*
* Add as dependencies all the assembly in the inheritance chain of a Type,
* including interfaces.
*/
private void AddBaseTypeDependencies(Type type) {
Assembly a = type.Module.Assembly;
// If the type is in a standard assembly, don't bother
if (a == typeof(string).Assembly || a == typeof(Page).Assembly || a == typeof(Uri).Assembly)
return;
AddAssemblyDependency(a);
// Recurse on the base Type
if (type.BaseType != null)
AddBaseTypeDependencies(type.BaseType);
// Recurse on all the implemented interfaces
Type[] interfaceTypes = type.GetInterfaces();
foreach (Type interfaceType in interfaceTypes)
AddBaseTypeDependencies(interfaceType);
}
/*
* Add an assembly that we must 'link' with in order to build
*/
internal Assembly AddAssemblyDependency(string assemblyName, bool addDependentAssemblies) {
Assembly assembly = LoadAssembly(assemblyName, !FInDesigner /*throwOnFail*/);
if (assembly != null)
AddAssemblyDependency(assembly, addDependentAssemblies);
return assembly;
}
internal Assembly AddAssemblyDependency(string assemblyName) {
return AddAssemblyDependency(assemblyName, false /*addDependentAssemblies*/);
}
/*
* Add an assembly that we must 'link' with in order to build
*/
internal void AddAssemblyDependency(Assembly assembly, bool addDependentAssemblies) {
if (_assemblyDependencies == null)
_assemblyDependencies = new AssemblySet();
if (_typeResolutionService != null)
_typeResolutionService.ReferenceAssembly(assembly.GetName());
_assemblyDependencies.Add(assembly);
// If addDependentAssemblies is true, add its dependent assemblies as well
if (addDependentAssemblies) {
AssemblySet assemblyDependencies = Util.GetReferencedAssemblies(assembly);
AddAssemblyDependencies(assemblyDependencies);
}
}
internal void AddAssemblyDependency(Assembly assembly) {
AddAssemblyDependency(assembly, false /*addDependentAssemblies*/);
}
/*
* Add a set of assemblies that we must 'link' with in order to build
*/
private void AddAssemblyDependencies(AssemblySet assemblyDependencies) {
if (assemblyDependencies == null)
return;
foreach (Assembly a in assemblyDependencies)
AddAssemblyDependency(a);
}
/// <internalonly/>
ICollection IAssemblyDependencyParser.AssemblyDependencies {
get {
return AssemblyDependencies;
}
}
internal IImplicitResourceProvider GetImplicitResourceProvider() {
//
if (FInDesigner)
return null;
// If we already attempted to get them, return whatever we got
if (flags[attemptedImplicitResources])
return _implicitResourceProvider;
flags[attemptedImplicitResources] = true;
IResourceProvider resourceProvider = ResourceExpressionBuilder.GetLocalResourceProvider(_rootBuilder.VirtualPath);
if (resourceProvider == null)
return null;
// If the resource provider is also an IImplicitResourceProvider, use that
_implicitResourceProvider = resourceProvider as IImplicitResourceProvider;
// Otherwise, use the default IImplicitResourceProvider implementation
if (_implicitResourceProvider == null)
_implicitResourceProvider = new DefaultImplicitResourceProvider(resourceProvider);
return _implicitResourceProvider;
}
}
/*
* Base class for classes that contain source file & line information for error reporting
*/
internal abstract class SourceLineInfo {
// Source file where the information appears
private string _virtualPath;
internal string VirtualPath {
get { return _virtualPath;}
set { _virtualPath = value;}
}
// Line number in the source file where the information appears
private int _line;
internal int Line {
get { return _line;}
set { _line = value;}
}
}
/*
* Objects that are placed on the BuilderStack
*/
internal class BuilderStackEntry: SourceLineInfo {
internal BuilderStackEntry (ControlBuilder builder,
string tagName, string virtualPath, int line,
string inputText, int textPos) {
_builder = builder;
_tagName = tagName;
VirtualPath = virtualPath;
Line = line;
_inputText = inputText;
_textPos = textPos;
}
internal ControlBuilder _builder;
internal string _tagName;
// the input string that contains the tag
internal string _inputText;
// Offset in the input string of the beginning of the tag's contents
internal int _textPos;
// Used to deal with non server tags nested in server tag with the same name
internal int _repeatCount;
}
/*
* Entry representing an import directive.
* e.g. <%@ import namespace="System.Web.UI" %>
*/
internal class NamespaceEntry: SourceLineInfo {
private string _namespace;
internal NamespaceEntry() {
}
internal string Namespace {
get { return _namespace;}
set { _namespace = value;}
}
}
internal class ScriptBlockData: SourceLineInfo {
protected string _script;
internal ScriptBlockData(int line, int column, string virtualPath) {
Line = line;
Column = column;
VirtualPath = virtualPath;
}
// Line number in the source file where the information appears
private int _column;
internal int Column {
get { return _column;}
set { _column = value;}
}
internal string Script {
get { return _script;}
set { _script = value;}
}
}
}
|