[asp.net] Ignore JavaScript blocks enclosed in HTML comments
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AssemblyBuilder.cs
index 19f3a4cba1761fc5ff4889977e84b8962b1a4770..99f704a34d703d8fce10a08dc4d9ee9e8e5f0eb6 100644 (file)
@@ -4,8 +4,9 @@
 // Authors:
 //     Chris Toshok (toshok@ximian.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//      Marek Habersack (mhabersack@novell.com)
 //
-// (C) 2006 Novell, Inc (http://www.novell.com)
+// (C) 2006-2008 Novell, Inc (http://www.novell.com)
 //
 
 //
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0
+
 
 using System;
 using System.CodeDom;
 using System.CodeDom.Compiler;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.IO;
+using System.Security.Cryptography;
 using System.Reflection;
+using System.Text;
 using System.Web.Configuration;
 using System.Web.Util;
+using System.Web.Hosting;
 
-namespace System.Web.Compilation {
+namespace System.Web.Compilation
+{
+       class CompileUnitPartialType
+       {
+               public readonly CodeCompileUnit Unit;
+               public readonly CodeNamespace ParentNamespace;
+               public readonly CodeTypeDeclaration PartialType;
 
-       public class AssemblyBuilder {
-               static bool KeepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
+               string typeName;
+               
+               public string TypeName {
+                       get {
+                               if (typeName == null) {
+                                       if (ParentNamespace == null || PartialType == null)
+                                               return null;
+                                       
+                                       typeName = ParentNamespace.Name;
+                                       if (String.IsNullOrEmpty (typeName))
+                                               typeName = PartialType.Name;
+                                       else
+                                               typeName += "." + PartialType.Name;
+                               }
+
+                               return typeName;
+                       }
+               }
+               
+               public CompileUnitPartialType (CodeCompileUnit unit, CodeNamespace parentNamespace, CodeTypeDeclaration type)
+               {
+                       this.Unit = unit;
+                       this.ParentNamespace = parentNamespace;
+                       this.PartialType = type;
+               }
+       }
+       
+       public class AssemblyBuilder
+       {
+               struct CodeUnit
+               {
+                       public readonly BuildProvider BuildProvider;
+                       public readonly CodeCompileUnit Unit;
+
+                       public CodeUnit (BuildProvider bp, CodeCompileUnit unit)
+                       {
+                               this.BuildProvider = bp;
+                               this.Unit = unit;
+                       }
+               }
 
+               interface ICodePragmaGenerator
+               {
+                       int ReserveSpace (string filename);
+                       void DecorateFile (string path, string filename, MD5 checksum, Encoding enc);
+               }
+
+               class CSharpCodePragmaGenerator : ICodePragmaGenerator
+               {
+                       // Copied from CSharpCodeGenerator.cs
+                       string QuoteSnippetString (string value)
+                       {
+                               // FIXME: this is weird, but works.
+                               string output = value.Replace ("\\", "\\\\");
+                               output = output.Replace ("\"", "\\\"");
+                               output = output.Replace ("\t", "\\t");
+                               output = output.Replace ("\r", "\\r");
+                               output = output.Replace ("\n", "\\n");
+                               
+                               return "\"" + output + "\"";
+                       }
+
+                       string ChecksumToHex (MD5 checksum)
+                       {
+                               var ret = new StringBuilder ();
+                               foreach (byte b in checksum.Hash)
+                                       ret.Append (b.ToString ("X2"));
+
+                               return ret.ToString ();
+                       }
+
+                       const int pragmaChecksumStaticCount = 23;
+                       const int pragmaLineStaticCount = 8;
+                       const int md5ChecksumCount = 32;
+                       
+                       public int ReserveSpace (string filename) 
+                       {
+                               return pragmaChecksumStaticCount +
+                                       pragmaLineStaticCount +
+                                       md5ChecksumCount +
+                                       (QuoteSnippetString (filename).Length * 2) +
+                                       (Environment.NewLine.Length * 3) +
+                                       BaseCompiler.HashMD5.ToString ("B").Length;
+                       }
+                       
+                       public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
+                       {
+                               string newline = Environment.NewLine;
+                               var sb = new StringBuilder ();
+                               
+                               sb.AppendFormat ("#pragma checksum {0} \"{1}\" \"{2}\"{3}{3}",
+                                                QuoteSnippetString (filename),
+                                                BaseCompiler.HashMD5.ToString ("B"),
+                                                ChecksumToHex (checksum),
+                                                newline);
+                               sb.AppendFormat ("#line 1 {0}{1}", QuoteSnippetString (filename), newline);
+
+                               byte[] bytes = enc.GetBytes (sb.ToString ());
+                               using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
+                                       fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
+                                       fs.Write (bytes, 0, bytes.Length);
+                                       bytes = null;
+                               
+                                       sb.Length = 0;
+                                       sb.AppendFormat ("{0}#line default{0}#line hidden{0}", newline);
+                                       bytes = Encoding.UTF8.GetBytes (sb.ToString ());
+                               
+                                       fs.Seek (0, SeekOrigin.End);
+                                       fs.Write (bytes, 0, bytes.Length);
+                               }
+                               
+                               sb = null;
+                               bytes = null;
+                       }
+               }
+
+               class VBCodePragmaGenerator : ICodePragmaGenerator
+               {
+                       const int pragmaExternalSourceCount = 21;
+                       public int ReserveSpace (string filename)
+                       {
+                               return pragmaExternalSourceCount +
+                                       filename.Length +
+                                       (Environment.NewLine.Length);
+                       }
+                       
+                       public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
+                       {
+                               string newline = Environment.NewLine;
+                               var sb = new StringBuilder ();
+
+                               sb.AppendFormat ("#ExternalSource(\"{0}\",1){1}", filename, newline);
+                               byte[] bytes = enc.GetBytes (sb.ToString ());
+                               using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
+                                       fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
+                                       fs.Write (bytes, 0, bytes.Length);
+                                       bytes = null;
+
+                                       sb.Length = 0;
+                                       sb.AppendFormat ("{0}#End ExternalSource{0}", newline);
+                                       bytes = enc.GetBytes (sb.ToString ());
+                                       fs.Seek (0, SeekOrigin.End);
+                                       fs.Write (bytes, 0, bytes.Length);
+                               }
+                               sb = null;
+                               bytes = null;
+                       }
+               }
+               
+               const string DEFAULT_ASSEMBLY_BASE_NAME = "App_Web_";
+               const int COPY_BUFFER_SIZE = 8192;
+               
+               static bool KeepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
+               
                CodeDomProvider provider;
-               List <CodeCompileUnit> units;
+               CompilerParameters parameters;
+
+               Dictionary <string, bool> code_files;
+               Dictionary <string, List <CompileUnitPartialType>> partial_types;
+               Dictionary <string, BuildProvider> path_to_buildprovider;
+               List <CodeUnit> units;
                List <string> source_files;
-               List <string> referenced_assemblies;
+               List <Assembly> referenced_assemblies;
                Dictionary <string, string> resource_files;
                TempFileCollection temp_files;
-               string virtual_path;
-               //TODO: there should be a Compile () method here which is where all the compilation exceptions are thrown from.
-
+               string outputFilesPrefix;
+               string outputAssemblyPrefix;
+               string outputAssemblyName;
+               
                internal AssemblyBuilder (CodeDomProvider provider)
-               : this (null, provider)
+               : this (null, provider, DEFAULT_ASSEMBLY_BASE_NAME)
+               {}
+
+               internal AssemblyBuilder (CodeDomProvider provider, string assemblyBaseName)
+               : this (null, provider, assemblyBaseName)
+               {}
+
+               internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider)
+               : this (virtualPath, provider, DEFAULT_ASSEMBLY_BASE_NAME)
                {}
                
-               internal AssemblyBuilder (string virtualPath, CodeDomProvider provider)
+               internal AssemblyBuilder (VirtualPath virtualPath, CodeDomProvider provider, string assemblyBaseName)
                {
                        this.provider = provider;
-                       this.virtual_path = virtualPath;
-                       units = new List <CodeCompileUnit> ();
-                       temp_files = new TempFileCollection ();
-                       referenced_assemblies = new List <string> ();
+                       this.outputFilesPrefix = assemblyBaseName ?? DEFAULT_ASSEMBLY_BASE_NAME;
+                       
+                       units = new List <CodeUnit> ();
+
                        CompilationSection section;
-                       if (virtualPath != null)
-                               section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation", virtualPath);
-                       else
-                               section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+                       section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
                        string tempdir = section.TempDirectory;
-                       if (tempdir == null || tempdir == "")
+                       if (String.IsNullOrEmpty (tempdir))
                                tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
-                               
+
+                       if (!KeepFiles)
+                               KeepFiles = section.Debug;
+                       
                        temp_files = new TempFileCollection (tempdir, KeepFiles);
                }
+
+               internal string OutputFilesPrefix {
+                       get {
+                               if (outputFilesPrefix == null)
+                                       outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
+
+                               return outputFilesPrefix;
+                       }
+
+                       set {
+                               if (String.IsNullOrEmpty (value))
+                                       outputFilesPrefix = DEFAULT_ASSEMBLY_BASE_NAME;
+                               else
+                                       outputFilesPrefix = value;
+                               outputAssemblyPrefix = null;
+                               outputAssemblyName = null;
+                       }
+               }
+               
+               internal string OutputAssemblyPrefix {
+                       get {
+                               if (outputAssemblyPrefix == null) {
+                                       string basePath = temp_files.BasePath;
+                                       string baseName = Path.GetFileName (basePath);
+                                       string baseDir = Path.GetDirectoryName (basePath);
+
+                                       outputAssemblyPrefix = Path.Combine (baseDir, String.Concat (OutputFilesPrefix, baseName));
+                               }
+
+                               return outputAssemblyPrefix;
+                       }
+               }
+
+               internal string OutputAssemblyName {
+                       get {
+                               if (outputAssemblyName == null)
+                                       outputAssemblyName = OutputAssemblyPrefix + ".dll";
+
+                               return outputAssemblyName;
+                       }
+               }
                
                internal TempFileCollection TempFiles {
                        get { return temp_files; }
                }
 
-               internal CodeCompileUnit [] GetUnitsAsArray ()
+               internal CompilerParameters CompilerOptions {
+                       get { return parameters; }
+                       set { parameters = value; }
+               }
+               
+               CodeUnit[] GetUnitsAsArray ()
                {
-                       CodeCompileUnit [] result = new CodeCompileUnit [units.Count];
+                       CodeUnit[] result = new CodeUnit [units.Count];
                        units.CopyTo (result, 0);
                        return result;
                }
-
+               
+               internal Dictionary <string, List <CompileUnitPartialType>> PartialTypes {
+                       get {
+                               if (partial_types == null)
+                                       partial_types = new Dictionary <string, List <CompileUnitPartialType>> ();
+                               return partial_types;
+                       }
+               }
+               
+               Dictionary <string, bool> CodeFiles {
+                       get {
+                               if (code_files == null)
+                                       code_files = new Dictionary <string, bool> ();
+                               return code_files;
+                       }
+               }
+               
                List <string> SourceFiles {
                        get {
                                if (source_files == null)
@@ -105,21 +343,79 @@ namespace System.Web.Compilation {
                        }
                }
 
+               internal BuildProvider GetBuildProviderForPhysicalFilePath (string path)
+               {
+                       if (String.IsNullOrEmpty (path) || path_to_buildprovider == null || path_to_buildprovider.Count == 0)
+                               return null;
+
+                       BuildProvider ret;
+                       if (path_to_buildprovider.TryGetValue (path, out ret))
+                               return ret;
+
+                       return null;
+               }
+               
                public void AddAssemblyReference (Assembly a)
                {
                        if (a == null)
                                throw new ArgumentNullException ("a");
 
-                       referenced_assemblies.Add (a.Location);
+                       List <Assembly> assemblies = ReferencedAssemblies;
+                       
+                       if (assemblies.Contains (a))
+                               return;
+                       
+                       assemblies.Add (a);
+               }
+
+               internal void AddAssemblyReference (string assemblyLocation)
+               {
+                       try {
+                               Assembly asm = Assembly.LoadFrom (assemblyLocation);
+                               if (asm == null)
+                                       return;
+
+                               AddAssemblyReference (asm);
+                       } catch {
+                               // ignore, it will come up later
+                       }
                }
 
+               internal void AddAssemblyReference (ICollection asmcoll)
+               {
+                       if (asmcoll == null || asmcoll.Count == 0)
+                               return;
+
+                       Assembly asm;
+                       foreach (object o in asmcoll) {
+                               asm = o as Assembly;
+                               if (asm == null)
+                                       continue;
+
+                               AddAssemblyReference (asm);
+                       }
+               }
+               
+               internal void AddAssemblyReference (List <Assembly> asmlist)
+               {
+                       if (asmlist == null)
+                               return;
+                       
+                       foreach (Assembly a in asmlist) {
+                               if (a == null)
+                                       continue;
+
+                               AddAssemblyReference (a);
+                       }
+               }
+               
                internal void AddCodeCompileUnit (CodeCompileUnit compileUnit)
                {
                        if (compileUnit == null)
                                throw new ArgumentNullException ("compileUnit");
-                       units.Add (compileUnit);
+                       units.Add (CheckForPartialTypes (new CodeUnit (null, compileUnit)));
                }
-               
+                               
                public void AddCodeCompileUnit (BuildProvider buildProvider, CodeCompileUnit compileUnit)
                {
                        if (buildProvider == null)
@@ -128,9 +424,20 @@ namespace System.Web.Compilation {
                        if (compileUnit == null)
                                throw new ArgumentNullException ("compileUnit");
 
-                       units.Add (compileUnit);
+                       units.Add (CheckForPartialTypes (new CodeUnit (buildProvider, compileUnit)));
                }
 
+               void AddPathToBuilderMap (string path, BuildProvider bp)
+               {
+                       if (path_to_buildprovider == null)
+                               path_to_buildprovider = new Dictionary <string, BuildProvider> ();
+
+                       if (path_to_buildprovider.ContainsKey (path))
+                               return;
+
+                       path_to_buildprovider.Add (path, bp);
+               }
+               
                public TextWriter CreateCodeFile (BuildProvider buildProvider)
                {
                        if (buildProvider == null)
@@ -139,20 +446,138 @@ namespace System.Web.Compilation {
                        // Generate a file name with the correct source language extension
                        string filename = GetTempFilePhysicalPath (provider.FileExtension);
                        SourceFiles.Add (filename);
-                       return new StreamWriter (File.OpenWrite (filename), WebEncoding.FileEncoding);
+                       AddPathToBuilderMap (filename, buildProvider);
+                       return new StreamWriter (File.OpenWrite (filename));
                }
 
                internal void AddCodeFile (string path)
                {
-                       if (path == null || path.Length == 0)
+                       AddCodeFile (path, null, false);
+               }
+
+               internal void AddCodeFile (string path, BuildProvider bp)
+               {
+                       AddCodeFile (path, bp, false);
+               }
+
+               // The kludge of using ICodePragmaGenerator for C# and VB code files is bad, but
+               // it's better than allowing for potential DoS while reading a file with arbitrary
+               // size in memory for use with the CodeSnippetCompileUnit class.
+               // Files with extensions other than .cs and .vb use CodeSnippetCompileUnit.
+               internal void AddCodeFile (string path, BuildProvider bp, bool isVirtual)
+               {
+                       if (String.IsNullOrEmpty (path))
+                               return;
+
+                       Dictionary <string, bool> codeFiles = CodeFiles;
+                       if (codeFiles.ContainsKey (path))
                                return;
+                       
+                       codeFiles.Add (path, true);
+                       
                        string extension = Path.GetExtension (path);
                        if (extension == null || extension.Length == 0)
                                return; // maybe better to throw an exception here?
                        extension = extension.Substring (1);
                        string filename = GetTempFilePhysicalPath (extension);
-                       File.Copy (path, filename, true);
-                       SourceFiles.Add (filename);
+                       ICodePragmaGenerator pragmaGenerator;
+                       
+                       switch (extension.ToLowerInvariant ()) {
+                               case "cs":
+                                       pragmaGenerator = new CSharpCodePragmaGenerator ();
+                                       break;
+
+                               case "vb":
+                                       pragmaGenerator = new VBCodePragmaGenerator ();
+                                       break;
+
+                               default:
+                                       pragmaGenerator = null;
+                                       break;
+                       }
+                       
+                       if (isVirtual) {
+                               VirtualFile vf = HostingEnvironment.VirtualPathProvider.GetFile (path);
+                               if (vf == null)
+                                       throw new HttpException (404, "Virtual file '" + path + "' does not exist.");
+
+                               if (vf is DefaultVirtualFile)
+                                       path = HostingEnvironment.MapPath (path);
+                               CopyFileWithChecksum (vf.Open (), filename, path, pragmaGenerator);
+                       } else
+                               CopyFileWithChecksum (path, filename, path, pragmaGenerator);
+
+                       if (pragmaGenerator != null) {
+                               if (bp != null)
+                                       AddPathToBuilderMap (filename, bp);
+                       
+                               SourceFiles.Add (filename);
+                       }
+               }
+
+               void CopyFileWithChecksum (string input, string to, string from, ICodePragmaGenerator pragmaGenerator)
+               {
+                       CopyFileWithChecksum (new FileStream (input, FileMode.Open, FileAccess.Read), to, from, pragmaGenerator);
+               }
+               
+               void CopyFileWithChecksum (Stream input, string to, string from, ICodePragmaGenerator pragmaGenerator)
+               {
+                       if (pragmaGenerator == null) {
+                               // This is BAD, BAD, BAD! CodeDOM API is really no good in this
+                               // instance.
+                               string filedata;
+                               using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
+                                       filedata = sr.ReadToEnd ();
+                               }
+
+                               var snippet = new CodeSnippetCompileUnit (filedata);
+                               snippet.LinePragma = new CodeLinePragma (from, 1);
+                               filedata = null;
+                               AddCodeCompileUnit (snippet);
+                               snippet = null;
+                               
+                               return;
+                       }
+                       
+                       MD5 checksum = MD5.Create ();
+                       using (FileStream fs = new FileStream (to, FileMode.Create, FileAccess.Write)) {
+                               using (StreamWriter sw = new StreamWriter (fs, Encoding.UTF8)) {
+                                       using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
+                                               int count = pragmaGenerator.ReserveSpace (from);
+                                               char[] src;
+                                               
+                                               if (count > COPY_BUFFER_SIZE)
+                                                       src = new char [count];
+                                               else
+                                                       src = new char [COPY_BUFFER_SIZE];
+
+                                               sw.Write (src, 0, count);
+                                               do {
+                                                       count = sr.Read (src, 0, COPY_BUFFER_SIZE);
+                                                       if (count == 0) {
+                                                               UpdateChecksum (src, 0, checksum, true);
+                                                               break;
+                                                       }
+                                               
+                                                       sw.Write (src, 0, count);
+                                                       UpdateChecksum (src, count, checksum, false);
+                                               } while (true);
+                                               src = null;
+                                       }
+                               }
+                       }
+                       pragmaGenerator.DecorateFile (to, from, checksum, Encoding.UTF8);
+               }
+
+               void UpdateChecksum (char[] buf, int count, MD5 checksum, bool final)
+               {
+                       byte[] input = Encoding.UTF8.GetBytes (buf, 0, count);
+
+                       if (final)
+                               checksum.TransformFinalBlock (input, 0, input.Length);
+                       else
+                               checksum.TransformBlock (input, 0, input.Length, input, 0);
+                       input = null;
                }
                
                public Stream CreateEmbeddedResource (BuildProvider buildProvider, string name)
@@ -179,26 +604,150 @@ namespace System.Web.Compilation {
                {
                        if (extension == null)
                                throw new ArgumentNullException ("extension");
-                       
-                       return temp_files.AddExtension (String.Format ("_{0}.{1}", temp_files.Count, extension), true);
+
+                       string newFileName = OutputAssemblyPrefix + "_" + temp_files.Count + "." + extension;
+                       temp_files.AddFile (newFileName, KeepFiles);
+
+                       return newFileName;
                }
 
                public CodeDomProvider CodeDomProvider {
                        get { return provider; }
                }
 
+               List <Assembly> ReferencedAssemblies {
+                       get {
+                               if (referenced_assemblies == null)
+                                       referenced_assemblies = new List <Assembly> ();
+
+                               return referenced_assemblies;
+                       }
+               }
+               
+               CodeUnit CheckForPartialTypes (CodeUnit codeUnit)
+               {
+                       CodeTypeDeclarationCollection types;
+                       CompileUnitPartialType partialType;
+                       string partialTypeName;
+                       List <CompileUnitPartialType> tmp;
+                       Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
+                       
+                       foreach (CodeNamespace ns in codeUnit.Unit.Namespaces) {
+                               if (ns == null)
+                                       continue;
+                               types = ns.Types;
+                               if (types == null || types.Count == 0)
+                                       continue;
+
+                               foreach (CodeTypeDeclaration type in types) {
+                                       if (type == null)
+                                               continue;
+
+                                       if (type.IsPartial) {
+                                               partialType = new CompileUnitPartialType (codeUnit.Unit, ns, type);
+                                               partialTypeName = partialType.TypeName;
+                                               
+                                               if (!partialTypes.TryGetValue (partialTypeName, out tmp)) {
+                                                       tmp = new List <CompileUnitPartialType> (1);
+                                                       partialTypes.Add (partialTypeName, tmp);
+                                               }
+                                               tmp.Add (partialType);
+                                       }
+                               }
+                       }
+                                               
+                       return codeUnit;
+               }
+               
+               void ProcessPartialTypes ()
+               {
+                       Dictionary <string, List <CompileUnitPartialType>> partialTypes = PartialTypes;
+                       if (partialTypes.Count == 0)
+                               return;
+                       
+                       foreach (KeyValuePair <string, List <CompileUnitPartialType>> kvp in partialTypes)
+                               ProcessType (kvp.Value);
+               }
+
+               void ProcessType (List <CompileUnitPartialType> typeList)
+               {
+                       CompileUnitPartialType[] types = new CompileUnitPartialType [typeList.Count];
+                       int counter = 0;
+                       
+                       foreach (CompileUnitPartialType type in typeList) {
+                               if (counter == 0) {
+                                       types [0] = type;
+                                       counter++;
+                                       continue;
+                               }
+
+                               for (int i = 0; i < counter; i++)
+                                       CompareTypes (types [i], type);
+                               types [counter++] = type;
+                       }
+               }
+
+               void CompareTypes (CompileUnitPartialType source, CompileUnitPartialType target)
+               {
+                       CodeTypeDeclaration sourceType = source.PartialType;
+                       CodeTypeMemberCollection targetMembers = target.PartialType.Members;
+                       List <CodeTypeMember> membersToRemove = new List <CodeTypeMember> ();
+                       
+                       foreach (CodeTypeMember member in targetMembers) {
+                               if (TypeHasMember (sourceType, member))
+                                       membersToRemove.Add (member);
+                       }
+
+                       foreach (CodeTypeMember member in membersToRemove)
+                               targetMembers.Remove (member);
+               }
+
+               bool TypeHasMember (CodeTypeDeclaration type, CodeTypeMember member)
+               {
+                       if (type == null || member == null)
+                               return false;
+
+                       return (FindMemberByName (type, member.Name) != null);
+               }
+
+               CodeTypeMember FindMemberByName (CodeTypeDeclaration type, string name)
+               {
+                       foreach (CodeTypeMember m in type.Members) {
+                               if (m == null || m.Name != name)
+                                       continue;
+                               return m;
+                       }
+
+                       return null;
+               }
+
+               internal CompilerResults BuildAssembly ()
+               {
+                       return BuildAssembly (null, CompilerOptions);
+               }
+               
+               internal CompilerResults BuildAssembly (VirtualPath virtualPath)
+               {
+                       return BuildAssembly (virtualPath, CompilerOptions);
+               }
+               
                internal CompilerResults BuildAssembly (CompilerParameters options)
                {
                        return BuildAssembly (null, options);
                }
                
-               internal CompilerResults BuildAssembly (string virtualPath, CompilerParameters options)
+               internal CompilerResults BuildAssembly (VirtualPath virtualPath, CompilerParameters options)
                {
                        if (options == null)
                                throw new ArgumentNullException ("options");
+                       options.TempFiles = temp_files;
+                       if (options.OutputAssembly == null)
+                               options.OutputAssembly = OutputAssemblyName;
+
+                       ProcessPartialTypes ();
                        
                        CompilerResults results;
-                       CodeCompileUnit [] units = GetUnitsAsArray ();
+                       CodeUnit [] units = GetUnitsAsArray ();
 
                        // Since we may have some source files and some code
                        // units, we generate code from all of them and then
@@ -206,13 +755,35 @@ namespace System.Web.Compilation {
                        // files. This also facilates possible debugging for the
                        // end user, since they get the code beforehand.
                        List <string> files = SourceFiles;
+                       Dictionary <string, string> resources = ResourceFiles;
+
+                       if (units.Length == 0 && files.Count == 0 && resources.Count == 0 && options.EmbeddedResources.Count == 0)
+                               return null;
+
+                       string compilerOptions = options.CompilerOptions;
+                       if (options.IncludeDebugInformation) {
+                               if (String.IsNullOrEmpty (compilerOptions))
+                                       compilerOptions = "/d:DEBUG";
+                               else if (compilerOptions.IndexOf ("d:DEBUG", StringComparison.OrdinalIgnoreCase) == -1)
+                                       compilerOptions += " /d:DEBUG";
+                               
+                               options.CompilerOptions = compilerOptions;
+                       }
+
+                       if (String.IsNullOrEmpty (compilerOptions))
+                               compilerOptions = "/noconfig";
+                       else if (compilerOptions.IndexOf ("noconfig", StringComparison.OrdinalIgnoreCase) == -1)
+                               compilerOptions += " /noconfig";
+                       options.CompilerOptions = compilerOptions;
+                       
                        string filename;
                        StreamWriter sw = null;
-                       foreach (CodeCompileUnit unit in units) {
+                       
+                       foreach (CodeUnit unit in units) {
                                filename = GetTempFilePhysicalPath (provider.FileExtension);
                                try {
-                                       sw = new StreamWriter (File.OpenWrite (filename), WebEncoding.FileEncoding);
-                                       provider.GenerateCodeFromCompileUnit (unit, sw, null);
+                                       sw = new StreamWriter (File.OpenWrite (filename), Encoding.UTF8);
+                                       provider.GenerateCodeFromCompileUnit (unit.Unit, sw, null);
                                        files.Add (filename);
                                } catch {
                                        throw;
@@ -222,28 +793,78 @@ namespace System.Web.Compilation {
                                                sw.Close ();
                                        }
                                }
+
+                               if (unit.BuildProvider != null)
+                                       AddPathToBuilderMap (filename, unit.BuildProvider);
                        }
-                       Dictionary <string, string> resources = ResourceFiles;
+
                        foreach (KeyValuePair <string, string> de in resources)
                                options.EmbeddedResources.Add (de.Value);
-                       foreach (string refasm in referenced_assemblies)
-                               options.ReferencedAssemblies.Add (refasm);
+
+                       AddAssemblyReference (BuildManager.GetReferencedAssemblies ());
+                       List <Assembly> referencedAssemblies = ReferencedAssemblies;
+                       StringCollection optRefAsm = options.ReferencedAssemblies;
+                       Type appType = HttpApplicationFactory.AppType;
+                       if (appType != null && !referencedAssemblies.Contains (appType.Assembly))
+                               referencedAssemblies.Add (appType.Assembly);
+
+                       foreach (Assembly refasm in ReferencedAssemblies) {
+                               string path = new Uri (refasm.CodeBase).LocalPath;
+                               string originalPath = refasm.Location;
+                               if (!optRefAsm.Contains (path) && !optRefAsm.Contains (originalPath))
+                                       optRefAsm.Add (path);
+                       }
+
                        
-                       results = provider.CompileAssemblyFromFile (options, files.ToArray ());
                        
-                       // FIXME: generate the code and display it
-                       if (results.NativeCompilerReturnValue != 0)
-                               throw new CompilationException (virtualPath, results.Errors, "");
+                       results = provider.CompileAssemblyFromFile (options, files.ToArray ());
+
+                       if (results.NativeCompilerReturnValue != 0) {
+                               string fileText = null;
+                               CompilerErrorCollection errors = results.Errors;
+                               try {
+                                       if (errors != null && errors.Count > 0) {
+                                               using (StreamReader sr = File.OpenText (results.Errors [0].FileName))
+                                                       fileText = sr.ReadToEnd ();
+                                       }
+                               } catch (Exception) {}
+                               
+#if DEBUG
+                               Console.WriteLine ("********************************************************************");
+                               Console.WriteLine ("Compilation failed.");
+                               Console.WriteLine ("Output:");
+                               foreach (string s in results.Output)
+                                       Console.WriteLine ("  " + s);
+                               Console.WriteLine ("\nErrors:");
+                               foreach (CompilerError err in results.Errors)
+                                       Console.WriteLine (err);
+                               if (errors != null && errors.Count > 0)
+                                       Console.WriteLine ("File name: {0}", results.Errors [0].FileName);
+                               else
+                                       Console.WriteLine ("File name not available");
+                               if (!String.IsNullOrEmpty (fileText))
+                                       Console.WriteLine ("File text:\n{0}\n", fileText);
+                               else
+                                       Console.WriteLine ("No file text available");
+                               Console.WriteLine ("********************************************************************");
+#endif
+                               throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results, fileText);
+                       }
                        
                        Assembly assembly = results.CompiledAssembly;
                        if (assembly == null) {
                                if (!File.Exists (options.OutputAssembly)) {
                                        results.TempFiles.Delete ();
-                                       throw new CompilationException (virtualPath, results.Errors,
+                                       throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results.Errors,
                                                "No assembly returned after compilation!?");
                                }
 
-                               results.CompiledAssembly = Assembly.LoadFrom (options.OutputAssembly);
+                               try {
+                                       results.CompiledAssembly = Assembly.LoadFrom (options.OutputAssembly);
+                               } catch (Exception ex) {
+                                       results.TempFiles.Delete ();
+                                       throw new HttpException ("Unable to load compiled assembly", ex);
+                               }
                        }
 
                        if (!KeepFiles)
@@ -252,5 +873,5 @@ namespace System.Web.Compilation {
                }
        }
 }
-#endif
+