|
//------------------------------------------------------------------------------
// <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"; }
}
}
}
|