2008-01-02 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / BaseCompiler.cs
index 71e8cc3da63f3001b40d32c315465e231b9bee26..802bd37b7597f43d84152196d53a1e1ec9c88e5d 100644 (file)
@@ -57,6 +57,8 @@ namespace System.Web.Compilation
 #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;
@@ -64,42 +66,132 @@ namespace System.Web.Compilation
 
                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 ();
 #if NET_2_0
                        if (parser.IsPartial) {
-                               string ns = null;
-                               string classtype = parser.PartialClassName;
+                               string partialns = null;
+                               string partialclasstype = parser.PartialClassName;
 
-                               if (classtype.Contains (".")) {
-                                       int dot = classtype.LastIndexOf (".");
-                                       ns = classtype.Substring (0, dot);
-                                       classtype = classtype.Substring (dot + 1);
+                               int partialdot = partialclasstype.LastIndexOf ('.');
+                               if (partialdot != -1) {
+                                       partialns = partialclasstype.Substring (0, partialdot);
+                                       partialclasstype = partialclasstype.Substring (partialdot + 1);
                                }
                                
-                               mainNS = new CodeNamespace (ns);
-                               mainClass = new CodeTypeDeclaration (classtype);
-                               mainClass.IsPartial = true;     
-                               mainClassExpr = new CodeTypeReferenceExpression (parser.PartialClassName);
-                       } else {
+                               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
-                       mainNS = new CodeNamespace ("ASP");
-                       mainClass = new CodeTypeDeclaration (parser.ClassName);
-                       CodeTypeReference baseTypeRef = new CodeTypeReference (parser.BaseType.FullName);
+
+                       string mainclasstype = parser.ClassName;
+                       string mainns = "ASP";
+
 #if NET_2_0
-                       if (parser.BaseTypeIsGlobal)
-                               baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
+                       int maindot = mainclasstype.LastIndexOf ('.');
+                       if (maindot != -1) {
+                               mainns = mainclasstype.Substring (0, maindot);
+                               mainclasstype = mainclasstype.Substring (maindot + 1);
+                       }
 #endif
-                       mainClass.BaseTypes.Add (baseTypeRef);
-                       mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
+
+                       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.TypeAttributes = TypeAttributes.Public;
                        mainNS.Types.Add (mainClass);
@@ -159,15 +251,18 @@ namespace System.Web.Compilation
                        CreateConstructor (null, null);
                }
 
-#if NET_2_0
-               internal CodeDomProvider Provider {
-                       get { return provider; }
-               }
+               internal CodeFieldReferenceExpression GetMainClassFieldReferenceExpression (string fieldName)
+               {
+                       CodeTypeReference mainClassTypeRef;
+                       mainClassTypeRef = new CodeTypeReference (mainNS.Name + "." + mainClass.Name);
 
-               internal CodeCompileUnit CompileUnit {
-                       get { return unit; }
-               }
+#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), "__initialized");
@@ -176,6 +271,35 @@ namespace System.Web.Compilation
                        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)
                {
@@ -186,15 +310,11 @@ namespace System.Web.Compilation
                        if (localVars != null)
                                ctor.Statements.AddRange (localVars);
 
-                       CodeTypeReferenceExpression r;
 #if NET_2_0
-                       if (parser.IsPartial)
-                               r = new CodeTypeReferenceExpression (mainClass.Name);
-                       else
+                       AssignAppRelativeVirtualPath (ctor);
 #endif
-                       r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
-                       CodeFieldReferenceExpression initialized;
-                       initialized = new CodeFieldReferenceExpression (r, "__initialized");
+
+                       CodeFieldReferenceExpression initialized = GetMainClassFieldReferenceExpression ("__initialized");
                        
                        CodeBinaryOperatorExpression bin;
                        bin = new CodeBinaryOperatorExpression (initialized,
@@ -204,9 +324,12 @@ namespace System.Web.Compilation
                        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);
                }
@@ -216,9 +339,15 @@ namespace System.Web.Compilation
                        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));
                        }
                }
                
@@ -246,14 +375,16 @@ namespace System.Web.Compilation
                        cast.Expression = refexp;
                        
                        property.GetStatements.Add (ret);
-                       mainClass.Members.Add (property);
+                       if (partialClass == null)
+                               mainClass.Members.Add (property);
+                       else
+                               partialClass.Members.Add (property);
                }
                
                protected void CreateProfileProperty ()
                {
                        string retType;
-                       ProfileSection ps = WebConfigurationManager.GetSection ("system.web/profile") as ProfileSection;
-                       if (ps != null && ps.PropertySettings.Count > 0)
+                       if (AppCodeCompiler.HaveCustomProfile (WebConfigurationManager.GetSection ("system.web/profile") as ProfileSection))
                                retType = "ProfileCommon";
                        else
                                retType = "System.Web.Profile.DefaultProfile";
@@ -396,9 +527,21 @@ namespace System.Web.Compilation
                        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 ()
@@ -406,81 +549,100 @@ namespace System.Web.Compilation
                        return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
                }
 
-               [MonoTODO ("find out how to extract the warningLevel and compilerOptions in the <system.codedom> case")]
-               public virtual Type GetCompiledType () 
+               internal static CodeDomProvider CreateProvider (string lang, out string compilerOptions, out int warningLevel, out string tempdir)
                {
-                       Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
-                       if (type != null)
-                               return type;
-
-                       Init ();
-                       string lang = parser.Language;
+                       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];
-
-                       string compilerOptions = "";
-                       int warningLevel = 0;
-
+                       
                        if (comp == null) {
                                CompilerInfo info = CodeDomProvider.GetCompilerInfo (lang);
-                               if (info != null && info.IsCodeDomProviderTypeValid)
-                                       provider = info.CreateProvider ();
-
-                               // XXX there's no way to get
-                               // warningLevel or compilerOptions out
-                               // of the provider.. they're in the
-                               // configuration section, though.
-                       }
-                       else {
-                               Type t = Type.GetType (comp.Type, true);
-                               provider = Activator.CreateInstance (t) as CodeDomProvider;
+                               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 (parser.Context);
-                       provider = config.GetProvider (lang);
+                       config = CompilationConfiguration.GetInstance (context);
+                       ret = config.GetProvider (lang);
 
-                       string compilerOptions = config.GetCompilerOptions (lang);
-                       int warningLevel = config.GetWarningLevel (lang);
+                       compilerOptions = config.GetCompilerOptions (lang);
+                       warningLevel = config.GetWarningLevel (lang);
 #endif
-                       if (provider == null)
+                       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);
+                       if (type != null)
+                               return type;
+
+                       Init ();
+                       string lang = parser.Language;
+                       string tempdir;
+                       string compilerOptions;
+                       int warningLevel;
+
+                       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
 
-                       compilerParameters.IncludeDebugInformation = parser.Debug;
-                       compilerParameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
-
-                       compilerParameters.WarningLevel = warningLevel;
+                       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 ();
@@ -568,16 +730,29 @@ namespace System.Web.Compilation
                }
 #endif
 
-               internal CompilerParameters CompilerParameters {
-                       get { return compilerParameters; }
+               internal CodeDomProvider Provider {
+                       get { return provider; }
+                       set { provider = value; }
                }
 
-               internal CodeCompileUnit Unit {
-                       get { return unit; }
+               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 virtual ICodeCompiler Compiler {
-                       get { return compiler; }
+               internal CodeCompileUnit CompileUnit {
+                       get { return unit; }
                }
 
                internal TemplateParser Parser {