using System.CodeDom.Compiler;
using System.Collections;
+using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Web.Configuration;
using System.Web.Util;
-namespace System.Web.UI {
+#if NET_2_0
+using System.Collections.Generic;
+#endif
+namespace System.Web.UI {
+ internal class ServerSideScript
+ {
+ public readonly string Script;
+ public readonly ILocation Location;
+
+ public ServerSideScript (string script, ILocation location)
+ {
+ Script = script;
+ Location = location;
+ }
+ }
+
// CAS
[AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
ArrayList interfaces;
ArrayList scripts;
Type baseType;
- bool baseTypeIsGlobal;
+ bool baseTypeIsGlobal = true;
string className;
RootBuilder rootBuilder;
bool debug;
string compilerOptions;
string language;
+ bool implicitLanguage;
bool strictOn = false;
bool explicitOn = false;
bool linePragmasOn = false;
bool output_cache;
int oc_duration;
string oc_header, oc_custom, oc_param, oc_controls;
+#if NET_2_0
+ string oc_content_encodings;
+#endif
bool oc_shared;
OutputCacheLocation oc_location;
CultureInfo invariantCulture = CultureInfo.InvariantCulture;
#if NET_2_0
string src;
+ bool srcIsLegacy;
string partialClassName;
+ string codeFileBaseClass;
+ string metaResourceKey;
+ Type codeFileBaseClassType;
+ List <UnknownAttributeDescriptor> unknownMainAttributes;
#endif
+ ILocation directiveLocation;
+
Assembly srcAssembly;
int appAssemblyIndex = -1;
internal TemplateParser ()
{
+ LoadConfigDefaults ();
+
imports = new ArrayList ();
#if NET_2_0
AddNamespaces (imports);
assemblies = new ArrayList ();
#if NET_2_0
+ CompilationSection compConfig = CompilationConfig;
+
bool addAssembliesInBin = false;
- foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
+ foreach (AssemblyInfo info in compConfig.Assemblies) {
if (info.Assembly == "*")
addAssembliesInBin = true;
else
imports.Add (info.Namespace);
}
#else
- foreach (string a in CompilationConfig.Assemblies)
+ CompilationConfiguration compConfig = CompilationConfig;
+
+ foreach (string a in compConfig.Assemblies)
AddAssemblyByName (a);
- if (CompilationConfig.AssembliesInBin)
+ if (compConfig.AssembliesInBin)
AddAssembliesInBin ();
#endif
- language = CompilationConfig.DefaultLanguage;
+ language = compConfig.DefaultLanguage;
+ implicitLanguage = true;
+ }
+
+ internal virtual void LoadConfigDefaults ()
+ {
+ debug = CompilationConfig.Debug;
}
internal void AddApplicationAssembly ()
if (!File.Exists (realpath))
throw new ParseException (Location, "Could not find file \"" + realpath + "\".");
- string vpath = UrlUtils.Combine (BaseVirtualDir, src);
+ string vpath = VirtualPathUtility.Combine (BaseVirtualDir, src);
+ if (VirtualPathUtility.IsAbsolute (vpath))
+ vpath = VirtualPathUtility.ToAppRelative (vpath);
+
Type type = null;
- AddDependency (realpath);
+ AddDependency (vpath);
try {
- ArrayList other_deps = new ArrayList ();
+#if NET_2_0
+ type = BuildManager.GetCompiledType (vpath);
+#else
+ ArrayList other_deps = new ArrayList ();
type = UserControlParser.GetCompiledType (vpath, realpath, other_deps, Context);
- foreach (string s in other_deps) {
+ foreach (string s in other_deps)
AddDependency (s);
- }
+#endif
} catch (ParseException pe) {
if (this is UserControlParser)
throw new ParseException (Location, pe.Message, pe);
internal void RegisterNamespace (string tagPrefix, string ns, string assembly)
{
AddImport (ns);
- Assembly ass = AddAssemblyByName (assembly);
- AddDependency (ass.Location);
+ Assembly ass = null;
+
+ if (assembly != null && assembly.Length > 0)
+ ass = AddAssemblyByName (assembly);
+
RootBuilder.Foundry.RegisterFoundry (tagPrefix, ass, ns);
}
if (atts ["Duration"] == null)
ThrowParseException ("The directive is missing a 'duration' attribute.");
- if (atts ["VaryByParam"] == null)
- ThrowParseException ("This directive is missing a 'VaryByParam' " +
- "attribute, which should be set to \"none\", \"*\", " +
+ if (atts ["VaryByParam"] == null && atts ["VaryByControl"] == null)
+ ThrowParseException ("This directive is missing 'VaryByParam' " +
+ "or 'VaryByControl' attribute, which should be set to \"none\", \"*\", " +
"or a list of name/value pairs.");
foreach (DictionaryEntry entry in atts) {
string key = (string) entry.Key;
switch (key.ToLower ()) {
- case "duration":
- oc_duration = Int32.Parse ((string) entry.Value);
- if (oc_duration < 1)
- ThrowParseException ("The 'duration' attribute must be set " +
- "to a positive integer value");
- break;
- case "varybyparam":
- oc_param = (string) entry.Value;
- if (String.Compare (oc_param, "none") == 0)
- oc_param = null;
- break;
- case "varybyheader":
- oc_header = (string) entry.Value;
- break;
- case "varybycustom":
- oc_custom = (string) entry.Value;
- break;
- case "location":
- if (!(this is PageParser))
- goto default;
-
- try {
- oc_location = (OutputCacheLocation) Enum.Parse (
- typeof (OutputCacheLocation), (string) entry.Value, true);
- } catch {
- ThrowParseException ("The 'location' attribute is case sensitive and " +
- "must be one of the following values: Any, Client, " +
- "Downstream, Server, None, ServerAndClient.");
- }
- break;
- case "varybycontrol":
- if (this is PageParser)
- goto default;
-
- oc_controls = (string) entry.Value;
- break;
- case "shared":
- if (this is PageParser)
- goto default;
-
- try {
- oc_shared = Boolean.Parse ((string) entry.Value);
- } catch {
- ThrowParseException ("The 'shared' attribute is case sensitive" +
- " and must be set to 'true' or 'false'.");
- }
- break;
- default:
- ThrowParseException ("The '" + key + "' attribute is not " +
- "supported by the 'Outputcache' directive.");
- break;
+ case "duration":
+ oc_duration = Int32.Parse ((string) entry.Value);
+ if (oc_duration < 1)
+ ThrowParseException ("The 'duration' attribute must be set " +
+ "to a positive integer value");
+ break;
+#if NET_2_0
+ case "varybycontentencodings":
+ oc_content_encodings = (string) entry.Value;
+ break;
+#endif
+ case "varybyparam":
+ oc_param = (string) entry.Value;
+ if (String.Compare (oc_param, "none") == 0)
+ oc_param = null;
+ break;
+ case "varybyheader":
+ oc_header = (string) entry.Value;
+ break;
+ case "varybycustom":
+ oc_custom = (string) entry.Value;
+ break;
+ case "location":
+ if (!(this is PageParser))
+ goto default;
+
+ try {
+ oc_location = (OutputCacheLocation) Enum.Parse (
+ typeof (OutputCacheLocation), (string) entry.Value, true);
+ } catch {
+ ThrowParseException ("The 'location' attribute is case sensitive and " +
+ "must be one of the following values: Any, Client, " +
+ "Downstream, Server, None, ServerAndClient.");
+ }
+ break;
+ case "varybycontrol":
+#if ONLY_1_1
+ if (this is PageParser)
+ goto default;
+#endif
+ oc_controls = (string) entry.Value;
+ break;
+ case "shared":
+ if (this is PageParser)
+ goto default;
+
+ try {
+ oc_shared = Boolean.Parse ((string) entry.Value);
+ } catch {
+ ThrowParseException ("The 'shared' attribute is case sensitive" +
+ " and must be set to 'true' or 'false'.");
+ }
+ break;
+ default:
+ ThrowParseException ("The '" + key + "' attribute is not " +
+ "supported by the 'Outputcache' directive.");
+ break;
}
}
internal Type LoadType (string typeName)
{
- // First try loaded assemblies, then try assemblies in Bin directory.
- Type type = null;
- bool seenBin = false;
- Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
- foreach (Assembly ass in assemblies) {
- type = ass.GetType (typeName);
- if (type == null)
- continue;
-
- if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
- AddAssembly (ass, true);
- } else {
- seenBin = true;
+ Type type = HttpApplication.LoadType (typeName);
+ if (type == null)
+ return null;
+ Assembly asm = type.Assembly;
+ string location = asm.Location;
+
+ string dirname = Path.GetDirectoryName (location);
+ bool doAddAssembly = true;
+ foreach (string dir in HttpApplication.BinDirectories) {
+ if (dirname == dir) {
+ doAddAssembly = false;
+ break;
}
-
- AddDependency (ass.Location);
- return type;
}
- if (seenBin)
- return null;
-
- // Load from bin
- if (!Directory.Exists (PrivateBinPath))
- return null;
-
- string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
- foreach (string s in binDlls) {
- Assembly binA = Assembly.LoadFrom (s);
- type = binA.GetType (typeName);
- if (type == null)
- continue;
+ if (doAddAssembly)
+ AddAssembly (asm, true);
- AddDependency (binA.Location);
- return type;
- }
-
- return null;
+ return type;
}
void AddAssembliesInBin ()
{
- if (!Directory.Exists (PrivateBinPath))
- return;
-
- string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
- foreach (string s in binDlls)
+ foreach (string s in HttpApplication.BinDirectoryAssemblies)
assemblies.Add (s);
}
-
+
internal virtual void AddInterface (string iface)
{
if (interfaces == null)
internal virtual void AddSourceDependency (string filename)
{
- if (dependencies != null && dependencies.Contains (filename)) {
+ if (dependencies != null && dependencies.Contains (filename))
ThrowParseException ("Circular file references are not allowed. File: " + filename);
- }
AddDependency (filename);
}
Assembly assembly = null;
Exception error = null;
- if (name.IndexOf (',') != -1) {
- try {
- assembly = Assembly.Load (name);
- } catch (Exception e) { error = e; }
- }
+ try {
+ assembly = Assembly.Load (name);
+ } catch (Exception e) { error = e; }
if (assembly == null) {
try {
AddAssembly (assembly, true);
return assembly;
}
-
+
internal virtual void ProcessMainAttributes (Hashtable atts)
{
+ directiveLocation = new System.Web.Compilation.Location (Location);
+
+#if NET_2_0
+ CompilationSection compConfig;
+#else
+ CompilationConfiguration compConfig;
+#endif
+
+ compConfig = CompilationConfig;
+
atts.Remove ("Description"); // ignored
#if NET_1_1
atts.Remove ("CodeBehind"); // ignored
#endif
atts.Remove ("AspCompat"); // ignored
-#if NET_2_0
- // these two are ignored for the moment
- atts.Remove ("Async");
- atts.Remove ("AsyncTimeOut");
-#endif
- debug = GetBool (atts, "Debug", true);
+ debug = GetBool (atts, "Debug", compConfig.Debug);
compilerOptions = GetString (atts, "CompilerOptions", "");
- language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
- strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
- explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
+ language = GetString (atts, "Language", "");
+ if (language.Length != 0)
+ implicitLanguage = false;
+ else
+ language = compConfig.DefaultLanguage;
+
+ strictOn = GetBool (atts, "Strict", compConfig.Strict);
+ explicitOn = GetBool (atts, "Explicit", compConfig.Explicit);
linePragmasOn = GetBool (atts, "LinePragmas", false);
string inherits = GetString (atts, "Inherits", null);
+ string srcRealPath = null;
+
#if NET_2_0
// In ASP 2, the source file is actually integrated with
// the generated file via the use of partial classes. This
// means that the code file has to be confirmed, but not
// used at this point.
src = GetString (atts, "CodeFile", null);
-
- if (src != null && inherits != null) {
+ codeFileBaseClass = GetString (atts, "CodeFileBaseClass", null);
+
+ if (src == null && codeFileBaseClass != null)
+ ThrowParseException ("The 'CodeFileBaseClass' attribute cannot be used without a 'CodeFile' attribute");
+
+ string legacySrc = GetString (atts, "Src", null);
+ if (legacySrc != null) {
+ legacySrc = UrlUtils.Combine (BaseVirtualDir, legacySrc);
+ if (src == null) {
+ src = legacySrc;
+ legacySrc = MapPath (legacySrc, false);
+ srcRealPath = legacySrc;
+ if (!File.Exists (srcRealPath))
+ ThrowParseException ("File " + src + " not found");
+
+ srcIsLegacy = true;
+ } else
+ legacySrc = MapPath (legacySrc, false);
+
+ GetAssemblyFromSource (legacySrc);
+ AddDependency (legacySrc);
+ }
+
+ if (!srcIsLegacy && src != null && inherits != null) {
// Make sure the source exists
src = UrlUtils.Combine (BaseVirtualDir, src);
- string realPath = MapPath (src, false);
- if (!File.Exists (realPath))
+ srcRealPath = MapPath (src, false);
+
+ if (!File.Exists (srcRealPath))
ThrowParseException ("File " + src + " not found");
// We are going to create a partial class that shares
// Add the code file as an option to the
// compiler. This lets both files be compiled at once.
- compilerOptions += " \"" + realPath + "\"";
+ compilerOptions += " \"" + srcRealPath + "\"";
+
+ if (codeFileBaseClass != null) {
+ try {
+ codeFileBaseClassType = LoadType (codeFileBaseClass);
+ } catch (Exception) {
+ }
+
+ if (codeFileBaseClassType == null)
+ ThrowParseException ("Could not load type '{0}'", codeFileBaseClass);
+ }
} else if (inherits != null) {
// We just set the inherits directly because this is a
// Single-Page model.
#else
string src = GetString (atts, "Src", null);
- if (src != null)
+ if (src != null) {
+ srcRealPath = MapPath (src, false);
srcAssembly = GetAssemblyFromSource (src);
-
+ }
+
if (inherits != null)
SetBaseType (inherits);
-
+#endif
+ if (src != null) {
+ if (VirtualPathUtility.IsAbsolute (src))
+ src = VirtualPathUtility.ToAppRelative (src);
+ AddDependency (src);
+ }
+
className = GetString (atts, "ClassName", null);
- if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
- ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
+ if (className != null) {
+#if NET_2_0
+ string [] identifiers = className.Split ('.');
+ for (int i = 0; i < identifiers.Length; i++)
+ if (!CodeGenerator.IsValidLanguageIndependentIdentifier (identifiers [i]))
+ ThrowParseException (String.Format ("'{0}' is not a valid "
+ + "value for attribute 'classname'.", className));
+#else
+ if (!CodeGenerator.IsValidLanguageIndependentIdentifier (className))
+ ThrowParseException (String.Format ("'{0}' is not a valid "
+ + "value for attribute 'classname'.", className));
#endif
+ }
+#if NET_2_0
+ if (this is TemplateControlParser)
+ metaResourceKey = GetString (atts, "meta:resourcekey", null);
+
+ if (inherits != null && (this is PageParser || this is UserControlParser) && atts.Count > 0) {
+ if (unknownMainAttributes == null)
+ unknownMainAttributes = new List <UnknownAttributeDescriptor> ();
+ string key, val;
+
+ foreach (DictionaryEntry de in atts) {
+ key = de.Key as string;
+ val = de.Value as string;
+
+ if (String.IsNullOrEmpty (key) || String.IsNullOrEmpty (val))
+ continue;
+ CheckUnknownAttribute (key, val, inherits);
+ }
+ return;
+ }
+#endif
if (atts.Count > 0)
ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
}
+#if NET_2_0
+ void CheckUnknownAttribute (string name, string val, string inherits)
+ {
+ MemberInfo mi = null;
+ bool missing = false;
+ string memberName = name.Trim ().ToLower (CultureInfo.InvariantCulture);
+ Type parent = codeFileBaseClassType;
+
+ if (parent == null)
+ parent = baseType;
+
+ try {
+ MemberInfo[] infos = parent.GetMember (memberName,
+ MemberTypes.Field | MemberTypes.Property,
+ BindingFlags.Public | BindingFlags.Instance |
+ BindingFlags.IgnoreCase | BindingFlags.Static);
+ if (infos.Length != 0) {
+ // prefer public properties to public methods (it's what MS.NET does)
+ foreach (MemberInfo tmp in infos) {
+ if (tmp is PropertyInfo) {
+ mi = tmp;
+ break;
+ }
+ }
+ if (mi == null)
+ mi = infos [0];
+ } else
+ missing = true;
+ } catch (Exception) {
+ missing = true;
+ }
+ if (missing)
+ ThrowParseException (
+ "Error parsing attribute '{0}': Type '{1}' does not have a public property named '{0}'",
+ memberName, inherits);
+
+ Type memberType = null;
+ if (mi is PropertyInfo) {
+ PropertyInfo pi = mi as PropertyInfo;
+
+ if (!pi.CanWrite)
+ ThrowParseException (
+ "Error parsing attribute '{0}': The '{0}' property is read-only and cannot be set.",
+ memberName);
+ memberType = pi.PropertyType;
+ } else if (mi is FieldInfo) {
+ memberType = ((FieldInfo)mi).FieldType;
+ } else
+ ThrowParseException ("Could not determine member the kind of '{0}' in base type '{1}",
+ memberName, inherits);
+ TypeConverter converter = TypeDescriptor.GetConverter (memberType);
+ bool convertible = true;
+ object value = null;
+
+ if (converter == null || !converter.CanConvertFrom (typeof (string)))
+ convertible = false;
+
+ if (convertible) {
+ try {
+ value = converter.ConvertFromInvariantString (val);
+ } catch (Exception) {
+ convertible = false;
+ }
+ }
+
+ if (!convertible)
+ ThrowParseException ("Error parsing attribute '{0}': Cannot create an object of type '{1}' from its string representation '{2}' for the '{3}' property.",
+ memberName, memberType, val, mi.Name);
+
+ UnknownAttributeDescriptor desc = new UnknownAttributeDescriptor (mi, value);
+ unknownMainAttributes.Add (desc);
+ }
+#endif
+
internal void SetBaseType (string type)
{
- if (type == DefaultBaseTypeName)
+ if (type == null || type == DefaultBaseTypeName) {
+ baseType = DefaultBaseType;
return;
-
+ }
+
Type parent = null;
if (srcAssembly != null)
parent = srcAssembly.GetType (type);
ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
baseType = parent;
- if (parent.FullName.IndexOf ('.') == -1)
- baseTypeIsGlobal = true;
}
+ internal void SetLanguage (string language)
+ {
+ this.language = language;
+ implicitLanguage = false;
+ }
+
Assembly GetAssemblyFromSource (string vpath)
{
vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
if (!File.Exists (realPath))
ThrowParseException ("File " + vpath + " not found");
- AddSourceDependency (realPath);
+ AddSourceDependency (vpath);
- CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
+ CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies, Debug);
if (result.NativeCompilerReturnValue != 0) {
StreamReader reader = new StreamReader (realPath);
throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
AddAssembly (result.CompiledAssembly, true);
return result.CompiledAssembly;
- }
-
- internal abstract Type DefaultBaseType { get; }
+ }
+
internal abstract string DefaultBaseTypeName { get; }
internal abstract string DefaultDirectiveName { get; }
+ internal Type DefaultBaseType {
+ get {
+ Type type = Type.GetType (DefaultBaseTypeName, true);
+
+ return type;
+ }
+ }
+
+ internal ILocation DirectiveLocation {
+ get { return directiveLocation; }
+ }
+
internal string InputFile
{
get { return inputFile; }
}
#if NET_2_0
- internal bool IsPartial
- {
- get { return src != null; }
+ internal bool IsPartial {
+ get { return (!srcIsLegacy && src != null); }
}
- internal string PartialClassName
- {
+ internal string CodeBehindSource {
+ get { return src; }
+ }
+
+ internal string PartialClassName {
get { return partialClassName; }
}
-#endif
- internal string Text
+ internal string CodeFileBaseClass {
+ get { return codeFileBaseClass; }
+ }
+
+ internal string MetaResourceKey {
+ get { return metaResourceKey; }
+ }
+
+ internal Type CodeFileBaseClassType
+ {
+ get { return codeFileBaseClassType; }
+ }
+
+ internal List <UnknownAttributeDescriptor> UnknownMainAttributes
{
+ get { return unknownMainAttributes; }
+ }
+#endif
+
+ internal string Text {
get { return text; }
set { text = value; }
}
- internal Type BaseType
- {
+ internal Type BaseType {
get {
if (baseType == null)
- baseType = DefaultBaseType;
-
+ SetBaseType (DefaultBaseTypeName);
+
return baseType;
}
}
internal bool BaseTypeIsGlobal {
get { return baseTypeIsGlobal; }
+ set { baseTypeIsGlobal = value; }
}
internal string ClassName {
if (className != null)
return className;
+#if NET_2_0
+ string physPath = HttpContext.Current.Request.PhysicalApplicationPath;
+ string inFile;
+
+ if (String.IsNullOrEmpty (inputFile)) {
+ inFile = null;
+ StreamReader sr = Reader as StreamReader;
+
+ if (sr != null) {
+ FileStream fr = sr.BaseStream as FileStream;
+ if (fr != null)
+ inFile = fr.Name;
+ }
+ } else
+ inFile = inputFile;
+
+ if (String.IsNullOrEmpty (inFile))
+ throw new HttpException ("Unable to determine class name - no input file found.");
+
+ if (StrUtils.StartsWith (inFile, physPath)) {
+ className = inputFile.Substring (physPath.Length).ToLower (CultureInfo.InvariantCulture);
+ className = className.Replace ('.', '_');
+ className = className.Replace ('/', '_').Replace ('\\', '_');
+ } else
+#endif
className = Path.GetFileName (inputFile).Replace ('.', '_');
className = className.Replace ('-', '_');
className = className.Replace (' ', '_');
}
}
- internal string PrivateBinPath {
- get {
- if (privateBinPath != null)
- return privateBinPath;
-
- AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
- privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
-
- return privateBinPath;
- }
- }
-
internal ArrayList Scripts {
get {
if (scripts == null)
get { return language; }
}
+ internal bool ImplicitLanguage {
+ get { return implicitLanguage; }
+ }
+
internal bool StrictOn {
get { return strictOn; }
}
get { return oc_duration; }
}
+#if NET_2_0
+ internal string OutputCacheVaryByContentEncodings {
+ get { return oc_content_encodings; }
+ }
+
+ internal virtual TextReader Reader {
+ get { return null; }
+ set { /* no-op */ }
+ }
+#endif
+
internal string OutputCacheVaryByHeader {
get { return oc_header; }
}