e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
562 lines
18 KiB
C#
562 lines
18 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="SimpleWebHandlerParser.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* Implements the parser for simple web handler files
|
|
*
|
|
* Copyright (c) 2000 Microsoft Corporation
|
|
*/
|
|
|
|
namespace System.Web.UI {
|
|
|
|
using System.Runtime.Serialization.Formatters;
|
|
using System.Text;
|
|
using System.Runtime.Serialization;
|
|
|
|
using System;
|
|
using System.Reflection;
|
|
using System.IO;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Text.RegularExpressions;
|
|
using System.CodeDom.Compiler;
|
|
using System.Web;
|
|
using System.Web.Hosting;
|
|
using System.Web.Caching;
|
|
using System.Web.Compilation;
|
|
using System.CodeDom;
|
|
using System.Web.Util;
|
|
using Debug=System.Web.Util.Debug;
|
|
using System.Web.RegularExpressions;
|
|
using System.Globalization;
|
|
using System.Security.Permissions;
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public abstract class SimpleWebHandlerParser : IAssemblyDependencyParser {
|
|
private readonly static Regex directiveRegex = new SimpleDirectiveRegex();
|
|
|
|
private SimpleHandlerBuildProvider _buildProvider;
|
|
|
|
private TextReader _reader;
|
|
|
|
private VirtualPath _virtualPath;
|
|
|
|
// The line number in file currently being parsed
|
|
private int _lineNumber;
|
|
|
|
// The column number in file currently being parsed
|
|
private int _startColumn;
|
|
|
|
private bool _fFoundMainDirective;
|
|
|
|
private string _typeName;
|
|
internal string TypeName {
|
|
get { return _typeName; }
|
|
}
|
|
|
|
private CompilerType _compilerType;
|
|
|
|
// The string containing the code to be compiled
|
|
private string _sourceString;
|
|
|
|
// Assemblies to be linked with
|
|
private AssemblySet _linkedAssemblies;
|
|
|
|
// The set of assemblies that the build system is telling us we will be linked with
|
|
private ICollection _referencedAssemblies;
|
|
|
|
private static char[] s_newlineChars = new char[] { '\r', '\n' };
|
|
|
|
private bool _ignoreParseErrors;
|
|
internal bool IgnoreParseErrors {
|
|
get { return _ignoreParseErrors; }
|
|
set { _ignoreParseErrors = value; }
|
|
}
|
|
|
|
internal void SetBuildProvider(SimpleHandlerBuildProvider buildProvider) {
|
|
_buildProvider = buildProvider;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
|
|
// Only allowed in full trust (ASURT 124397)
|
|
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
|
|
protected SimpleWebHandlerParser(HttpContext context, string virtualPath, string physicalPath) {
|
|
|
|
// These obsolete parameters should never be set
|
|
Debug.Assert(context == null);
|
|
Debug.Assert(physicalPath == null);
|
|
|
|
Debug.Assert(virtualPath != null);
|
|
|
|
_virtualPath = VirtualPath.Create(virtualPath);
|
|
}
|
|
|
|
/*
|
|
* Compile a web handler file into a Type. Result is cached.
|
|
*/
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
protected Type GetCompiledTypeFromCache() {
|
|
|
|
//
|
|
// This method is practically useless, but cannot be removed to avoid a breaking change
|
|
//
|
|
|
|
BuildResultCompiledType result = (BuildResultCompiledType) BuildManager.GetVPathBuildResult(_virtualPath);
|
|
|
|
return result.ResultType;
|
|
}
|
|
|
|
internal void Parse(ICollection referencedAssemblies) {
|
|
|
|
_referencedAssemblies = referencedAssemblies;
|
|
|
|
AddSourceDependency(_virtualPath);
|
|
|
|
// Open a TextReader for the virtualPath we're parsing
|
|
using (_reader = _buildProvider.OpenReaderInternal()) {
|
|
ParseReader();
|
|
}
|
|
}
|
|
|
|
internal CompilerType CompilerType { get { return _compilerType; } }
|
|
|
|
internal ICollection AssemblyDependencies { get { return _linkedAssemblies; } }
|
|
private StringSet _sourceDependencies;
|
|
internal ICollection SourceDependencies { get { return _sourceDependencies; } }
|
|
|
|
internal CodeCompileUnit GetCodeModel() {
|
|
|
|
// Do we have something to compile?
|
|
if (_sourceString == null)
|
|
return null;
|
|
|
|
CodeSnippetCompileUnit snippetCompileUnit = new CodeSnippetCompileUnit(_sourceString);
|
|
|
|
// Put in some context so that the file can be debugged.
|
|
snippetCompileUnit.LinePragma = BaseCodeDomTreeGenerator.CreateCodeLinePragmaHelper(
|
|
_virtualPath.VirtualPathString, _lineNumber);
|
|
|
|
return snippetCompileUnit;
|
|
}
|
|
|
|
internal IDictionary GetLinePragmasTable() {
|
|
LinePragmaCodeInfo codeInfo = new LinePragmaCodeInfo();
|
|
codeInfo._startLine = _lineNumber;
|
|
codeInfo._startColumn = _startColumn;
|
|
codeInfo._startGeneratedColumn = 1;
|
|
codeInfo._codeLength = -1;
|
|
codeInfo._isCodeNugget = false;
|
|
|
|
IDictionary linePragmasTable = new Hashtable();
|
|
linePragmasTable[_lineNumber] = codeInfo;
|
|
|
|
return linePragmasTable;
|
|
}
|
|
|
|
internal bool HasInlineCode { get { return (_sourceString != null); } }
|
|
|
|
internal Type GetTypeToCache(Assembly builtAssembly) {
|
|
Type t = null;
|
|
|
|
// First, try to get the type from the assembly that has been built (if any)
|
|
if (builtAssembly != null)
|
|
t = builtAssembly.GetType(_typeName);
|
|
|
|
// If not, try to get it from other assemblies
|
|
if (t == null)
|
|
t = GetType(_typeName);
|
|
|
|
// Make sure the type derives from what we expect
|
|
try {
|
|
ValidateBaseType(t);
|
|
}
|
|
catch (Exception e) {
|
|
throw new HttpParseException(e.Message, e, _virtualPath, _sourceString, _lineNumber);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
internal virtual void ValidateBaseType(Type t) {
|
|
// No restriction on the base type by default
|
|
}
|
|
|
|
/*
|
|
* Parse the contents of the TextReader
|
|
*/
|
|
private void ParseReader() {
|
|
string s = _reader.ReadToEnd();
|
|
|
|
try {
|
|
ParseString(s);
|
|
}
|
|
catch (Exception e) {
|
|
throw new HttpParseException(e.Message, e, _virtualPath, s, _lineNumber);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the contents of the string
|
|
*/
|
|
private void ParseString(string text) {
|
|
int textPos = 0;
|
|
Match match;
|
|
_lineNumber = 1;
|
|
|
|
// First, parse all the <%@ ... %> directives
|
|
for (;;) {
|
|
match = directiveRegex.Match(text, textPos);
|
|
|
|
// Done with the directives?
|
|
if (!match.Success)
|
|
break;
|
|
|
|
_lineNumber += Util.LineCount(text, textPos, match.Index);
|
|
textPos = match.Index;
|
|
|
|
// Get all the directives into a bag
|
|
IDictionary directive = CollectionsUtil.CreateCaseInsensitiveSortedList();
|
|
string directiveName = ProcessAttributes(match, directive);
|
|
|
|
ProcessDirective(directiveName, directive);
|
|
|
|
_lineNumber += Util.LineCount(text, textPos, match.Index + match.Length);
|
|
textPos = match.Index + match.Length;
|
|
|
|
int newlineIndex = text.LastIndexOfAny(s_newlineChars, textPos-1);
|
|
_startColumn = textPos - newlineIndex;
|
|
}
|
|
|
|
if (!_fFoundMainDirective && !IgnoreParseErrors) {
|
|
throw new HttpException(
|
|
SR.GetString(SR.Missing_directive, DefaultDirectiveName));
|
|
}
|
|
|
|
// skip the directive
|
|
string remainingText = text.Substring(textPos);
|
|
|
|
// If there is something else in the file, it needs to be compiled
|
|
if (!Util.IsWhiteSpaceString(remainingText))
|
|
_sourceString = remainingText;
|
|
}
|
|
|
|
private string ProcessAttributes(Match match, IDictionary attribs) {
|
|
string ret = String.Empty;
|
|
CaptureCollection attrnames = match.Groups["attrname"].Captures;
|
|
CaptureCollection attrvalues = match.Groups["attrval"].Captures;
|
|
CaptureCollection equalsign = null;
|
|
equalsign = match.Groups["equal"].Captures;
|
|
|
|
for (int i = 0; i < attrnames.Count; i++) {
|
|
string attribName = attrnames[i].ToString();
|
|
string attribValue = attrvalues[i].ToString();
|
|
|
|
// Check if there is an equal sign.
|
|
bool fHasEqual = (equalsign[i].ToString().Length > 0);
|
|
|
|
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 (!fHasEqual && i==0) {
|
|
ret = attribName;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
if (attribs != null)
|
|
attribs.Add(attribName, attribValue);
|
|
}
|
|
catch (ArgumentException) {
|
|
|
|
// Ignore the duplicate attributes when called from CBM
|
|
if (IgnoreParseErrors) continue;
|
|
|
|
throw new HttpException(
|
|
SR.GetString(SR.Duplicate_attr_in_tag, attribName));
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
protected abstract string DefaultDirectiveName { get; }
|
|
|
|
private static void ProcessCompilationParams(IDictionary directive, CompilerParameters compilParams) {
|
|
bool fDebug = false;
|
|
if (Util.GetAndRemoveBooleanAttribute(directive, "debug", ref fDebug))
|
|
compilParams.IncludeDebugInformation = fDebug;
|
|
|
|
if (compilParams.IncludeDebugInformation &&
|
|
!HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Medium)) {
|
|
throw new HttpException(SR.GetString(SR.Insufficient_trust_for_attribute, "debug"));
|
|
}
|
|
|
|
int warningLevel=0;
|
|
if (Util.GetAndRemoveNonNegativeIntegerAttribute(directive, "warninglevel", ref warningLevel)) {
|
|
compilParams.WarningLevel = warningLevel;
|
|
if (warningLevel > 0)
|
|
compilParams.TreatWarningsAsErrors = true;
|
|
}
|
|
|
|
string compilerOptions = Util.GetAndRemoveNonEmptyAttribute(
|
|
directive, "compileroptions");
|
|
if (compilerOptions != null) {
|
|
CompilationUtil.CheckCompilerOptionsAllowed(compilerOptions, false /*config*/, null, 0);
|
|
compilParams.CompilerOptions = compilerOptions;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process a <%@ %> block
|
|
*/
|
|
internal virtual void ProcessDirective(string directiveName, IDictionary directive) {
|
|
|
|
// Empty means default
|
|
if (directiveName.Length == 0)
|
|
directiveName = DefaultDirectiveName;
|
|
|
|
// Check for the main directive
|
|
if (IsMainDirective(directiveName)) {
|
|
|
|
// Make sure the main directive was not already specified
|
|
if (_fFoundMainDirective && !IgnoreParseErrors) {
|
|
throw new HttpException(
|
|
SR.GetString(SR.Only_one_directive_allowed, DefaultDirectiveName));
|
|
}
|
|
|
|
_fFoundMainDirective = true;
|
|
|
|
// Since description is a no op, just remove it if it's there
|
|
directive.Remove("description");
|
|
|
|
// Similarily, ignore 'codebehind' attribute (ASURT 4591)
|
|
directive.Remove("codebehind");
|
|
|
|
string language = Util.GetAndRemoveNonEmptyAttribute(directive, "language");
|
|
|
|
// Get the compiler for the specified language (if any)
|
|
if (language != null) {
|
|
_compilerType = _buildProvider.GetDefaultCompilerTypeForLanguageInternal(language);
|
|
}
|
|
else {
|
|
// Get a default from config
|
|
_compilerType = _buildProvider.GetDefaultCompilerTypeInternal();
|
|
}
|
|
|
|
_typeName = Util.GetAndRemoveRequiredAttribute(directive, "class");
|
|
|
|
if (_compilerType.CompilerParameters != null)
|
|
ProcessCompilationParams(directive, _compilerType.CompilerParameters);
|
|
}
|
|
else if (StringUtil.EqualsIgnoreCase(directiveName, "assembly")) {
|
|
// Assembly directive
|
|
|
|
// Remove the attributes as we get them from the dictionary
|
|
string assemblyName = Util.GetAndRemoveNonEmptyAttribute(directive, "name");
|
|
VirtualPath src = Util.GetAndRemoveVirtualPathAttribute(directive, "src");
|
|
|
|
if (assemblyName != null && src != null && !IgnoreParseErrors) {
|
|
throw new HttpException(
|
|
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 if (!IgnoreParseErrors) {
|
|
throw new HttpException(SR.GetString(SR.Missing_attr, "name"));
|
|
}
|
|
}
|
|
else if (!IgnoreParseErrors) {
|
|
throw new HttpException(
|
|
SR.GetString(SR.Unknown_directive, directiveName));
|
|
}
|
|
|
|
// If there are some attributes left, fail
|
|
Util.CheckUnknownDirectiveAttributes(directiveName, directive);
|
|
}
|
|
|
|
internal virtual bool IsMainDirective(string directiveName) {
|
|
return (string.Compare(directiveName, DefaultDirectiveName,
|
|
StringComparison.OrdinalIgnoreCase) == 0);
|
|
}
|
|
|
|
/*
|
|
* Compile a source file into an assembly, and import it
|
|
*/
|
|
private void ImportSourceFile(VirtualPath virtualPath) {
|
|
|
|
// Get a full path to the source file
|
|
VirtualPath baseVirtualDir = _virtualPath.Parent;
|
|
VirtualPath fullVirtualPath = baseVirtualDir.Combine(virtualPath);
|
|
|
|
// Add the source file to the list of files we depend on
|
|
AddSourceDependency(fullVirtualPath);
|
|
|
|
//
|
|
|
|
CompilationUtil.GetCompilerInfoFromVirtualPath(fullVirtualPath);
|
|
|
|
// Compile it into an assembly
|
|
|
|
BuildResultCompiledAssembly result = (BuildResultCompiledAssembly) BuildManager.GetVPathBuildResult(
|
|
fullVirtualPath);
|
|
Assembly a = result.ResultAssembly;
|
|
|
|
// Add a dependency to the assembly
|
|
AddAssemblyDependency(a);
|
|
}
|
|
|
|
/*
|
|
* Add a file as a dependency for the DLL we're building
|
|
*/
|
|
internal void AddSourceDependency(VirtualPath fileName) {
|
|
if (_sourceDependencies == null)
|
|
_sourceDependencies = new CaseInsensitiveStringSet();
|
|
|
|
_sourceDependencies.Add(fileName.VirtualPathString);
|
|
}
|
|
|
|
private void AddAssemblyDependency(string assemblyName) {
|
|
|
|
// Load and keep track of the assembly
|
|
Assembly a = Assembly.Load(assemblyName);
|
|
|
|
AddAssemblyDependency(a);
|
|
}
|
|
|
|
private void AddAssemblyDependency(Assembly assembly) {
|
|
|
|
if (_linkedAssemblies == null)
|
|
_linkedAssemblies = new AssemblySet();
|
|
_linkedAssemblies.Add(assembly);
|
|
}
|
|
|
|
/*
|
|
* Look for a type by name in the assemblies available to this page
|
|
*/
|
|
private Type GetType(string typeName) {
|
|
|
|
Type t;
|
|
|
|
// If it contains an assembly name, just call Type.GetType (ASURT 53589)
|
|
if (Util.TypeNameContainsAssembly(typeName)) {
|
|
try {
|
|
t = Type.GetType(typeName, true);
|
|
}
|
|
catch (Exception e) {
|
|
throw new HttpParseException(null, e, _virtualPath, _sourceString, _lineNumber);
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
t = Util.GetTypeFromAssemblies(_referencedAssemblies, typeName, false /*ignoreCase*/);
|
|
if (t != null)
|
|
return t;
|
|
|
|
t = Util.GetTypeFromAssemblies(_linkedAssemblies, typeName, false /*ignoreCase*/);
|
|
if (t != null)
|
|
return t;
|
|
|
|
throw new HttpParseException(
|
|
SR.GetString(SR.Could_not_create_type, typeName),
|
|
null, _virtualPath, _sourceString, _lineNumber);
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
ICollection IAssemblyDependencyParser.AssemblyDependencies {
|
|
get {
|
|
return AssemblyDependencies;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
internal class WebHandlerParser: SimpleWebHandlerParser {
|
|
|
|
internal WebHandlerParser(string virtualPath)
|
|
: base(null /*context*/, virtualPath, null /*physicalPath*/) {}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
protected override string DefaultDirectiveName {
|
|
get { return "webhandler"; }
|
|
}
|
|
|
|
internal override void ValidateBaseType(Type t) {
|
|
// Make sure the type has the correct base class
|
|
Util.CheckAssignableType(typeof(IHttpHandler), t);
|
|
}
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public class WebServiceParser: SimpleWebHandlerParser {
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
|
|
// Only allowed in full trust (ASURT 123890)
|
|
[SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
|
|
public static Type GetCompiledType(string inputFile, HttpContext context) {
|
|
|
|
// NOTE: the inputFile parameter should be named virtualPath, but cannot be changed
|
|
// as it would be a minor breaking change! (VSWhidbey 80997).
|
|
BuildResultCompiledType result = (BuildResultCompiledType) BuildManager.GetVPathBuildResult(
|
|
context, VirtualPath.Create(inputFile));
|
|
|
|
return result.ResultType;
|
|
}
|
|
|
|
internal WebServiceParser(string virtualPath)
|
|
: base(null /*context*/, virtualPath, null /*physicalPath*/) { }
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
protected override string DefaultDirectiveName {
|
|
get { return "webservice"; }
|
|
}
|
|
}
|
|
|
|
}
|