using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
+using System.Collections.Specialized;
using System.Reflection;
using System.Text;
using System.Web.UI;
{
abstract class BaseCompiler
{
+#if NET_2_0
+ static BindingFlags replaceableFlags = BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Instance;
+#endif
+
TemplateParser parser;
CodeDomProvider provider;
ICodeCompiler compiler;
CodeCompileUnit unit;
CodeNamespace mainNS;
CompilerParameters compilerParameters;
+#if NET_2_0
+ bool isRebuilding = false;
+ protected Hashtable partialNameOverride = new Hashtable();
+ protected CodeTypeDeclaration partialClass;
+ protected CodeTypeReferenceExpression partialClassExpr;
+#endif
protected CodeTypeDeclaration mainClass;
protected CodeTypeReferenceExpression mainClassExpr;
protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
protected BaseCompiler (TemplateParser parser)
{
- compilerParameters = new CompilerParameters ();
this.parser = parser;
}
+ internal CodeStatement AddLinePragma (CodeExpression expression, ControlBuilder builder)
+ {
+ return AddLinePragma (new CodeExpressionStatement (expression), builder);
+ }
+
+ internal CodeStatement AddLinePragma (CodeStatement statement, ControlBuilder builder)
+ {
+ if (builder == null || statement == null)
+ return statement;
+
+ ILocation location = null;
+
+ if (!(builder is CodeRenderBuilder))
+ location = builder.location;
+
+ if (location != null)
+ return AddLinePragma (statement, location);
+ else
+ return AddLinePragma (statement, builder.line, builder.fileName);
+ }
+
+ internal CodeStatement AddLinePragma (CodeStatement statement, ILocation location)
+ {
+ if (location == null || statement == null)
+ return statement;
+
+ return AddLinePragma (statement, location.BeginLine, location.Filename);
+ }
+
+ internal CodeStatement AddLinePragma (CodeStatement statement, int line, string fileName)
+ {
+ if (statement == null)
+ return null;
+
+ statement.LinePragma = new CodeLinePragma (fileName, line);
+ return statement;
+ }
+
+ internal CodeTypeMember AddLinePragma (CodeTypeMember member, ControlBuilder builder)
+ {
+ if (builder == null || member == null)
+ return member;
+
+ ILocation location = builder.location;
+
+ if (location != null)
+ return AddLinePragma (member, location);
+ else
+ return AddLinePragma (member, builder.line, builder.fileName);
+ }
+
+ internal CodeTypeMember AddLinePragma (CodeTypeMember member, ILocation location)
+ {
+ if (location == null || member == null)
+ return member;
+
+ return AddLinePragma (member, location.BeginLine, location.Filename);
+ }
+
+ internal CodeTypeMember AddLinePragma (CodeTypeMember member, int line, string fileName)
+ {
+ if (member == null)
+ return null;
+
+ member.LinePragma = new CodeLinePragma (fileName, line);
+ return member;
+ }
+
void Init ()
{
unit = new CodeCompileUnit ();
- mainNS = new CodeNamespace ("ASP");
+#if NET_2_0
+ if (parser.IsPartial) {
+ string partialns = null;
+ string partialclasstype = parser.PartialClassName;
+
+ int partialdot = partialclasstype.LastIndexOf ('.');
+ if (partialdot != -1) {
+ partialns = partialclasstype.Substring (0, partialdot);
+ partialclasstype = partialclasstype.Substring (partialdot + 1);
+ }
+
+ CodeNamespace partialNS = new CodeNamespace (partialns);
+ partialClass = new CodeTypeDeclaration (partialclasstype);
+ partialClass.IsPartial = true;
+ partialClassExpr = new CodeTypeReferenceExpression (parser.PartialClassName);
+
+ unit.Namespaces.Add (partialNS);
+ partialClass.TypeAttributes = TypeAttributes.Public;
+ partialNS.Types.Add (partialClass);
+ }
+#endif
+
+ string mainclasstype = parser.ClassName;
+ string mainns = "ASP";
+
+#if NET_2_0
+ int maindot = mainclasstype.LastIndexOf ('.');
+ if (maindot != -1) {
+ mainns = mainclasstype.Substring (0, maindot);
+ mainclasstype = mainclasstype.Substring (maindot + 1);
+ }
+#endif
+
+ mainNS = new CodeNamespace (mainns);
+ mainClass = new CodeTypeDeclaration (mainclasstype);
+ CodeTypeReference baseTypeRef;
+#if NET_2_0
+ if (partialClass != null) {
+ baseTypeRef = new CodeTypeReference (parser.PartialClassName);
+ baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
+ } else {
+ baseTypeRef = new CodeTypeReference (parser.BaseType.FullName);
+ if (parser.BaseTypeIsGlobal)
+ baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
+ }
+#else
+ baseTypeRef = new CodeTypeReference (parser.BaseType.FullName);
+#endif
+ mainClass.BaseTypes.Add (baseTypeRef);
+
+ mainClassExpr = new CodeTypeReferenceExpression (mainns + "." + mainclasstype);
+
unit.Namespaces.Add (mainNS);
- mainClass = new CodeTypeDeclaration (parser.ClassName);
mainClass.TypeAttributes = TypeAttributes.Public;
mainNS.Types.Add (mainClass);
- mainClass.BaseTypes.Add (new CodeTypeReference (parser.BaseType.FullName));
- mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
+
foreach (object o in parser.Imports) {
if (o is string)
mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
}
+ // StringCollection.Contains has O(n) complexity, but
+ // considering the number of comparisons we make on
+ // average and the fact that using an intermediate array
+ // would be even more costly, this is fine here.
+ StringCollection refAsm = unit.ReferencedAssemblies;
+ string asmName;
if (parser.Assemblies != null) {
foreach (object o in parser.Assemblies) {
- if (o is string)
- unit.ReferencedAssemblies.Add ((string) o);
+ asmName = o as string;
+ if (asmName != null && !refAsm.Contains (asmName))
+ refAsm.Add (asmName);
}
}
+#if NET_2_0
+ ArrayList al = WebConfigurationManager.ExtraAssemblies;
+ if (al != null && al.Count > 0) {
+ foreach (object o in al) {
+ asmName = o as string;
+ if (asmName != null && !refAsm.Contains (asmName))
+ refAsm.Add (asmName);
+ }
+ }
+
+ IList list = BuildManager.CodeAssemblies;
+ if (list != null && list.Count > 0) {
+ Assembly asm;
+ foreach (object o in list) {
+ asm = o as Assembly;
+ if (o == null)
+ continue;
+ asmName = asm.Location;
+ if (asmName != null && !refAsm.Contains (asmName))
+ refAsm.Add (asmName);
+ }
+ }
+#endif
// Late-bound generators specifics (as for MonoBASIC/VB.NET)
unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
unit.UserData["AllowLateBound"] = !parser.StrictOn;
CreateStaticFields ();
AddApplicationAndSessionObjects ();
AddScripts ();
+ CreateMethods ();
CreateConstructor (null, null);
}
+ internal CodeFieldReferenceExpression GetMainClassFieldReferenceExpression (string fieldName)
+ {
+ CodeTypeReference mainClassTypeRef;
+ mainClassTypeRef = new CodeTypeReference (mainNS.Name + "." + mainClass.Name);
+
+#if NET_2_0
+ mainClassTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
+#endif
+ return new CodeFieldReferenceExpression (
+ new CodeTypeReferenceExpression (mainClassTypeRef), fieldName);
+ }
+
protected virtual void CreateStaticFields ()
{
- CodeMemberField fld = new CodeMemberField (typeof (bool), "__intialized");
+ CodeMemberField fld = new CodeMemberField (typeof (bool), "__initialized");
fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
fld.InitExpression = new CodePrimitiveExpression (false);
mainClass.Members.Add (fld);
}
+#if NET_2_0
+ void AssignAppRelativeVirtualPath (CodeConstructor ctor)
+ {
+ Type baseType = parser.CodeFileBaseClassType;
+
+ if (baseType == null)
+ baseType = parser.BaseType;
+ if (baseType == null)
+ return;
+ if (!baseType.IsSubclassOf (typeof (System.Web.UI.TemplateControl)))
+ return;
+
+ string arvp = Path.Combine (parser.BaseVirtualDir, Path.GetFileName (parser.InputFile));
+ if (VirtualPathUtility.IsAbsolute (arvp))
+ arvp = "~" + arvp;
+
+ CodeTypeReference baseTypeRef = new CodeTypeReference (baseType.FullName);
+ if (parser.BaseTypeIsGlobal)
+ baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
+
+ CodeExpression cast = new CodeCastExpression (baseTypeRef, new CodeThisReferenceExpression ());
+ CodePropertyReferenceExpression arvpProp = new CodePropertyReferenceExpression (cast, "AppRelativeVirtualPath");
+ CodeAssignStatement arvpAssign = new CodeAssignStatement ();
+ arvpAssign.Left = arvpProp;
+ arvpAssign.Right = new CodePrimitiveExpression (VirtualPathUtility.RemoveTrailingSlash (arvp));
+ ctor.Statements.Add (arvpAssign);
+ }
+#endif
+
protected virtual void CreateConstructor (CodeStatementCollection localVars,
CodeStatementCollection trueStmt)
{
if (localVars != null)
ctor.Statements.AddRange (localVars);
- CodeTypeReferenceExpression r;
- r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
- CodeFieldReferenceExpression intialized;
- intialized = new CodeFieldReferenceExpression (r, "__intialized");
+#if NET_2_0
+ AssignAppRelativeVirtualPath (ctor);
+#endif
+
+ CodeFieldReferenceExpression initialized = GetMainClassFieldReferenceExpression ("__initialized");
CodeBinaryOperatorExpression bin;
- bin = new CodeBinaryOperatorExpression (intialized,
+ bin = new CodeBinaryOperatorExpression (initialized,
CodeBinaryOperatorType.ValueEquality,
new CodePrimitiveExpression (false));
- CodeAssignStatement assign = new CodeAssignStatement (intialized,
+ CodeAssignStatement assign = new CodeAssignStatement (initialized,
new CodePrimitiveExpression (true));
- CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
+ CodeConditionStatement cond = new CodeConditionStatement ();
+ cond.Condition = bin;
+
if (trueStmt != null)
cond.TrueStatements.AddRange (trueStmt);
+ cond.TrueStatements.Add (assign);
ctor.Statements.Add (cond);
}
if (parser.Scripts == null || parser.Scripts.Count == 0)
return;
+ ServerSideScript sss;
+
foreach (object o in parser.Scripts) {
- if (o is string)
- mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
+ sss = o as ServerSideScript;
+
+ if (sss == null)
+ continue;
+
+ mainClass.Members.Add (AddLinePragma (new CodeSnippetTypeMember (sss.Script), sss.Location));
}
}
- protected virtual void CreateMethods ()
+ protected internal virtual void CreateMethods ()
{
}
+#if NET_2_0
+ void InternalCreatePageProperty (string retType, string name, string contextProperty)
+ {
+ CodeMemberProperty property = new CodeMemberProperty ();
+ property.Name = name;
+ property.Type = new CodeTypeReference (retType);
+ property.Attributes = MemberAttributes.Family | MemberAttributes.Final;
+
+ CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
+ CodeCastExpression cast = new CodeCastExpression ();
+ ret.Expression = cast;
+
+ CodePropertyReferenceExpression refexp = new CodePropertyReferenceExpression ();
+ refexp.TargetObject = new CodePropertyReferenceExpression (new CodeThisReferenceExpression (), "Context");
+ refexp.PropertyName = contextProperty;
+
+ cast.TargetType = new CodeTypeReference (retType);
+ cast.Expression = refexp;
+
+ property.GetStatements.Add (ret);
+ if (partialClass == null)
+ mainClass.Members.Add (property);
+ else
+ partialClass.Members.Add (property);
+ }
+
+ protected void CreateProfileProperty ()
+ {
+ string retType;
+ if (AppCodeCompiler.HaveCustomProfile (WebConfigurationManager.GetSection ("system.web/profile") as ProfileSection))
+ retType = "ProfileCommon";
+ else
+ retType = "System.Web.Profile.DefaultProfile";
+ InternalCreatePageProperty (retType, "Profile", "Profile");
+ }
+#endif
+
protected virtual void AddInterfaces ()
{
if (parser.Interfaces == null)
if (results.NativeCompilerReturnValue == 0)
return;
- StringWriter writer = new StringWriter();
- provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
- throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
+ string fileText = null;
+ CompilerErrorCollection errors = results.Errors;
+ CompilerError ce = (errors != null && errors.Count > 0) ? errors [0] : null;
+ string inFile = (ce != null) ? ce.FileName : null;
+
+ if (inFile != null && File.Exists (inFile)) {
+ using (StreamReader sr = File.OpenText (inFile)) {
+ fileText = sr.ReadToEnd ();
+ }
+ } else {
+ StringWriter writer = new StringWriter();
+ provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
+ fileText = writer.ToString ();
+ }
+ throw new CompilationException (parser.InputFile, errors, fileText);
}
protected string DynamicDir ()
{
return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
}
+
+ internal static CodeDomProvider CreateProvider (string lang, out string compilerOptions, out int warningLevel, out string tempdir)
+ {
+ return CreateProvider (HttpContext.Current, lang, out compilerOptions, out warningLevel, out tempdir);
+ }
+
+ internal static CodeDomProvider CreateProvider (HttpContext context, string lang, out string compilerOptions, out int warningLevel, out string tempdir)
+ {
+ CodeDomProvider ret = null;
+
+#if NET_2_0
+ CompilationSection config = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+ Compiler comp = config.Compilers[lang];
+
+ if (comp == null) {
+ CompilerInfo info = CodeDomProvider.GetCompilerInfo (lang);
+ if (info != null && info.IsCodeDomProviderTypeValid) {
+ ret = info.CreateProvider ();
+
+ CompilerParameters cp = info.CreateDefaultCompilerParameters ();
+ compilerOptions = cp.CompilerOptions;
+ warningLevel = cp.WarningLevel;
+ } else {
+ compilerOptions = String.Empty;
+ warningLevel = 2;
+ }
+ } else {
+ Type t = HttpApplication.LoadType (comp.Type, true);
+ ret = Activator.CreateInstance (t) as CodeDomProvider;
+
+ compilerOptions = comp.CompilerOptions;
+ warningLevel = comp.WarningLevel;
+ }
+#else
+ CompilationConfiguration config;
+
+ config = CompilationConfiguration.GetInstance (context);
+ ret = config.GetProvider (lang);
+
+ compilerOptions = config.GetCompilerOptions (lang);
+ warningLevel = config.GetWarningLevel (lang);
+#endif
+ tempdir = config.TempDirectory;
+
+ return ret;
+ }
+ [MonoTODO ("find out how to extract the warningLevel and compilerOptions in the <system.codedom> case")]
public virtual Type GetCompiledType ()
{
Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
Init ();
string lang = parser.Language;
- CompilationConfiguration config;
+ string tempdir;
+ string compilerOptions;
+ int warningLevel;
- config = CompilationConfiguration.GetInstance (parser.Context);
- provider = config.GetProvider (lang);
- if (provider == null)
+ Provider = CreateProvider (parser.Context, lang, out compilerOptions, out warningLevel, out tempdir);
+ if (Provider == null)
throw new HttpException ("Configuration error. Language not supported: " +
lang, 500);
+#if !NET_2_0
compiler = provider.CreateCompiler ();
+#endif
- CreateMethods ();
- compilerParameters.IncludeDebugInformation = parser.Debug;
- compilerParameters.CompilerOptions = config.GetCompilerOptions (lang) + " " +
- parser.CompilerOptions;
-
- compilerParameters.WarningLevel = config.GetWarningLevel (lang);
+ CompilerParameters parameters = CompilerParameters;
+ parameters.IncludeDebugInformation = parser.Debug;
+ parameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
+ parameters.WarningLevel = warningLevel;
+
bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
- string tempdir = config.TempDirectory;
if (tempdir == null || tempdir == "")
tempdir = DynamicDir ();
TempFileCollection tempcoll = new TempFileCollection (tempdir, keepFiles);
- compilerParameters.TempFiles = tempcoll;
+ parameters.TempFiles = tempcoll;
string dllfilename = Path.GetFileName (tempcoll.AddExtension ("dll", true));
- compilerParameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
+ parameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
CompilerResults results = CachingCompiler.Compile (this);
CheckCompilerErrors (results);
Assembly assembly = results.CompiledAssembly;
if (assembly == null) {
- if (!File.Exists (compilerParameters.OutputAssembly))
+ if (!File.Exists (parameters.OutputAssembly)) {
+ results.TempFiles.Delete ();
throw new CompilationException (parser.InputFile, results.Errors,
"No assembly returned after compilation!?");
+ }
- assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
+ assembly = Assembly.LoadFrom (parameters.OutputAssembly);
}
results.TempFiles.Delete ();
- return assembly.GetType (mainClassExpr.Type.BaseType, true);
+ Type mainClassType = assembly.GetType (mainClassExpr.Type.BaseType, true);
+
+#if NET_2_0
+ if (parser.IsPartial) {
+ // With the partial classes, we need to make sure we
+ // don't have any methods that should have not been
+ // created (because they are accessible from the base
+ // types). We cannot do this normally because the
+ // codebehind file is actually a partial class and we
+ // have no way of identifying the partial class' base
+ // type until now.
+ if (!isRebuilding && CheckPartialBaseType (mainClassType)) {
+ isRebuilding = true;
+ parser.RootBuilder.ResetState ();
+ return GetCompiledType ();
+ }
+ }
+#endif
+
+ return mainClassType;
}
- internal CompilerParameters CompilerParameters {
- get { return compilerParameters; }
+#if NET_2_0
+ internal bool IsRebuildingPartial
+ {
+ get { return isRebuilding; }
}
- internal CodeCompileUnit Unit {
- get { return unit; }
+ internal bool CheckPartialBaseType (Type type)
+ {
+ // Get the base type. If we don't have any (bad thing), we
+ // don't need to replace ourselves. Also check for the
+ // core file, since that won't have any either.
+ Type baseType = type.BaseType;
+ if (baseType == null || baseType == typeof(System.Web.UI.Page))
+ return false;
+
+ bool rebuild = false;
+
+ if (CheckPartialBaseFields (type, baseType))
+ rebuild = true;
+
+ if (CheckPartialBaseProperties (type, baseType))
+ rebuild = true;
+
+ return rebuild;
+ }
+
+ internal bool CheckPartialBaseFields (Type type, Type baseType)
+ {
+ bool rebuild = false;
+
+ foreach (FieldInfo baseInfo in baseType.GetFields (replaceableFlags)) {
+ if (baseInfo.IsPrivate)
+ continue;
+
+ FieldInfo typeInfo = type.GetField (baseInfo.Name, replaceableFlags);
+
+ if (typeInfo != null && typeInfo.DeclaringType == type) {
+ partialNameOverride [typeInfo.Name] = true;
+ rebuild = true;
+ }
+ }
+
+ return rebuild;
}
- internal virtual ICodeCompiler Compiler {
+ internal bool CheckPartialBaseProperties (Type type, Type baseType)
+ {
+ bool rebuild = false;
+
+ foreach (PropertyInfo baseInfo in baseType.GetProperties ()) {
+ PropertyInfo typeInfo = type.GetProperty (baseInfo.Name);
+
+ if (typeInfo != null && typeInfo.DeclaringType == type) {
+ partialNameOverride [typeInfo.Name] = true;
+ rebuild = true;
+ }
+ }
+
+ return rebuild;
+ }
+#endif
+
+ internal CodeDomProvider Provider {
+ get { return provider; }
+ set { provider = value; }
+ }
+
+ internal ICodeCompiler Compiler {
get { return compiler; }
+ set { compiler = value; }
+ }
+
+ internal CompilerParameters CompilerParameters {
+ get {
+ if (compilerParameters == null)
+ compilerParameters = new CompilerParameters ();
+
+ return compilerParameters;
+ }
+
+ set { compilerParameters = value; }
+ }
+
+ internal CodeCompileUnit CompileUnit {
+ get { return unit; }
}
internal TemplateParser Parser {