2009-02-28 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Compilation / AppCodeCompiler.cs
index 86994858cd1bb678cb5111521fc146a61f957d84..71ac1d1452e111e5f1ed05c970e0d84f14726e29 100644 (file)
@@ -44,15 +44,57 @@ using System.Web.Util;
 
 namespace System.Web.Compilation
 {
+       class AssemblyPathResolver
+       {
+               static Dictionary <string, string> assemblyCache;
+
+               static AssemblyPathResolver ()
+               {
+                       assemblyCache = new Dictionary <string, string> ();
+               }
+
+               public static string GetAssemblyPath (string assemblyName)
+               {
+                       lock (assemblyCache) {
+                               if (assemblyCache.ContainsKey (assemblyName))
+                                       return assemblyCache [assemblyName];
+
+                               Assembly asm = null;
+                               Exception error = null;
+                               if (assemblyName.IndexOf (',') != -1) {
+                                       try {
+                                               asm = Assembly.Load (assemblyName);
+                                       } catch (Exception e) {
+                                               error = e;
+                                       }
+                               }
+
+                               if (asm == null) {
+                                       try {
+                                               asm = Assembly.LoadWithPartialName (assemblyName);
+                                       } catch (Exception e) {
+                                               error = e;
+                                       }
+                               }
+                        
+                               if (asm == null)
+                                       throw new HttpException (String.Format ("Unable to find assembly {0}", assemblyName), error);
+
+                               assemblyCache.Add (assemblyName, asm.Location);
+                               return asm.Location;
+                       }
+               }
+       }
+       
        internal class AppCodeAssembly
        {
-               private List<string> files;
-               private List<CodeCompileUnit> units;
+               List<string> files;
+               List<CodeCompileUnit> units;
                
-               private string name;
-               private string path;
-               private bool validAssembly;
-               private string outputAssemblyName;
+               string name;
+               string path;
+               bool validAssembly;
+               string outputAssemblyName;
 
                public string OutputAssemblyName
                {
@@ -121,13 +163,13 @@ namespace System.Web.Compilation
                        
                        // First make sure all the files are in the same
                        // language
-                       bool known;
+                       bool known = false;
                        foreach (string f in files) {
                                known = true;
                                language = null;
                                
                                extension = Path.GetExtension (f);
-                               if (!CodeDomProvider.IsDefinedExtension (extension))
+                               if (String.IsNullOrEmpty (extension) || !CodeDomProvider.IsDefinedExtension (extension))
                                        known = false;
                                if (known) {
                                        language = CodeDomProvider.GetLanguageFromExtension(extension);
@@ -156,16 +198,13 @@ namespace System.Web.Compilation
                        }
 
                        CodeDomProvider provider = null;
-                       CompilationSection compilationSection = WebConfigurationManager.GetSection ("system.web/compilation") as CompilationSection;
+                       CompilationSection compilationSection = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
                        if (compilerInfo == null) {
                                if (!CodeDomProvider.IsDefinedLanguage (compilationSection.DefaultLanguage))
                                        throw new HttpException ("Failed to retrieve default source language");
                                compilerInfo = CodeDomProvider.GetCompilerInfo (compilationSection.DefaultLanguage);
                                if (compilerInfo == null || !compilerInfo.IsCodeDomProviderTypeValid)
                                        throw new HttpException ("Internal error while initializing application");
-                               provider = compilerInfo.CreateProvider ();
-                               if (provider == null)
-                                       throw new HttpException ("A code provider error occurred while initializing application.");
                        }
 
                        provider = compilerInfo.CreateProvider ();
@@ -186,12 +225,11 @@ namespace System.Web.Compilation
                                parameters.ReferencedAssemblies.AddRange (binAssemblies);
                        
                        if (compilationSection != null) {
-                               AssemblyName asmName;
                                foreach (AssemblyInfo ai in compilationSection.Assemblies)
                                        if (ai.Assembly != "*") {
                                                try {
-                                                       asmName = new AssemblyName (ai.Assembly);
-                                                       parameters.ReferencedAssemblies.Add (asmName.Name);
+                                                       parameters.ReferencedAssemblies.Add (
+                                                               AssemblyPathResolver.GetAssemblyPath (ai.Assembly));
                                                } catch (Exception ex) {
                                                        throw new HttpException (
                                                                String.Format ("Could not find assembly {0}.", ai.Assembly),
@@ -208,6 +246,9 @@ namespace System.Web.Compilation
                                        bprovider.GenerateCode (abuilder);
                                }
                        }
+
+                       if (knownfiles.Count == 0 && unknownfiles.Count == 0 && units.Count == 0)
+                               return;
                        
                        outputAssemblyName = (string)FileUtils.CreateTemporaryFile (
                                AppDomain.CurrentDomain.SetupInformation.DynamicBase,
@@ -216,31 +257,40 @@ namespace System.Web.Compilation
                        foreach (Assembly a in BuildManager.TopLevelAssemblies)
                                parameters.ReferencedAssemblies.Add (a.Location);
                        CompilerResults results = abuilder.BuildAssembly (parameters);
-                       if (results.Errors.Count == 0) {
-                               BuildManager.CodeAssemblies.Add (results.PathToAssembly);
+                       if (results == null)
+                               return;
+                       
+                       if (results.NativeCompilerReturnValue == 0) {
+                               BuildManager.CodeAssemblies.Add (results.CompiledAssembly);
                                BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly);
+                               HttpRuntime.WritePreservationFile (results.CompiledAssembly, name);
                        } else {
                                if (HttpContext.Current.IsCustomErrorEnabled)
                                        throw new HttpException ("An error occurred while initializing application.");
                                throw new CompilationException (null, results.Errors, null);
                        }
                }
-
-               private BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
+               
+               VirtualPath PhysicalToVirtual (string file)
+               {
+                       return new VirtualPath (file.Replace (HttpRuntime.AppDomainAppPath, "/").Replace (Path.DirectorySeparatorChar, '/'));
+               }
+               
+               BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
                {
                        if (file == null || file.Length == 0 || buildProviders == null || buildProviders.Count == 0)
                                return null;
 
                        BuildProvider ret = buildProviders.GetProviderForExtension (Path.GetExtension (file));
                        if (ret != null && IsCorrectBuilderType (ret)) {
-                               ret.SetVirtualPath (VirtualPathUtility.ToAppRelative (file));
+                               ret.SetVirtualPath (PhysicalToVirtual (file));
                                return ret;
                        }
                                
                        return null;
                }
 
-               private bool IsCorrectBuilderType (BuildProvider bp)
+               bool IsCorrectBuilderType (BuildProvider bp)
                {
                        if (bp == null)
                                return false;
@@ -273,7 +323,7 @@ namespace System.Web.Compilation
        
        internal class AppCodeCompiler
        {
-               static private bool _alreadyCompiled;
+               static bool _alreadyCompiled;
                internal static string DefaultAppCodeAssemblyName;
                
                // A dictionary that contains an entry per an assembly that will
@@ -298,8 +348,8 @@ namespace System.Web.Compilation
                // Files for which exist BuildProviders but which have no
                // unambiguous language assigned to them (e.g. .wsdl files), are
                // built using the default website compiler.
-               private List<AppCodeAssembly> assemblies;
-               string _bindir;
+               List<AppCodeAssembly> assemblies;
+               string providerTypeName = null;
                
                public AppCodeCompiler ()
                {
@@ -309,12 +359,12 @@ namespace System.Web.Compilation
                bool ProcessAppCodeDir (string appCode, AppCodeAssembly defasm)
                {
                        // First process the codeSubDirectories
-                       CompilationSection cs = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+                       CompilationSection cs = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
                        
                        if (cs != null) {
                                string aname;
                                for (int i = 0; i < cs.CodeSubDirectories.Count; i++) {
-                                       aname = String.Format ("App_SubCode_{0}", cs.CodeSubDirectories[i].DirectoryName);
+                                       aname = String.Concat ("App_SubCode_", cs.CodeSubDirectories[i].DirectoryName);
                                        assemblies.Add (new AppCodeAssembly (
                                                                aname,
                                                                Path.Combine (appCode, cs.CodeSubDirectories[i].DirectoryName)));
@@ -331,28 +381,6 @@ namespace System.Web.Compilation
                        return new CodeTypeReference (type);
                }
 
-               Type GetTypeFromBin (string typeName)
-               {
-                       string bindir = BinDir;
-                       if (!Directory.Exists (bindir))
-                               return null;
-                       
-                       string [] binDlls = Directory.GetFiles (bindir, "*.dll");
-                       Type ret = null;
-                       foreach (string dll in binDlls) {
-                               try {
-                                       Assembly asm = Assembly.LoadFrom (dll);
-                                       ret = asm.GetType (typeName, false);
-                                       if (ret != null)
-                                               break;
-                               } catch (Exception) {
-                                       continue;
-                               }
-                       }
-
-                       return ret;
-               }
-
                string FindProviderTypeName (ProfileSection ps, string providerName)
                {
                        if (ps.Providers == null || ps.Providers.Count == 0)
@@ -367,23 +395,13 @@ namespace System.Web.Compilation
                void GetProfileProviderAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
                                                  string providerName)
                {
-                       string providerTypeName;
-
                        if (String.IsNullOrEmpty (providerName))
                                providerTypeName = FindProviderTypeName (ps, ps.DefaultProvider);
                        else
                                providerTypeName = FindProviderTypeName (ps, providerName);
                        if (providerTypeName == null)
-                               throw new HttpException (String.Format ("Profile provider type not found: {0}",
-                                                                       providerTypeName));
-                       
-                       Type type = Type.GetType (providerTypeName, false);
-                       if (type == null) {
-                               type = GetTypeFromBin (providerTypeName);
-                               if (type == null)
-                                       throw new HttpException (String.Format ("Profile provider type not found: {0}",
-                                                                               providerTypeName));
-                       }
+                               throw new HttpException (String.Format ("Profile provider type not defined: {0}",
+                                                                       providerName));
                        
                        collection.Add (
                                new CodeAttributeDeclaration (
@@ -398,7 +416,7 @@ namespace System.Web.Compilation
                void GetProfileSettingsSerializeAsAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
                                                             SerializationMode mode)
                {
-                       string parameter = String.Format ("SettingsSerializeAs.{0}", mode.ToString ());
+                       string parameter = String.Concat ("SettingsSerializeAs.", mode.ToString ());
                        collection.Add (
                                new CodeAttributeDeclaration (
                                        "SettingsSerializeAs",
@@ -409,6 +427,32 @@ namespace System.Web.Compilation
                        );
                                        
                }
+
+               void AddProfileClassGetProfileMethod (CodeTypeDeclaration profileClass)
+               {
+                       CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
+                               new CodeTypeReferenceExpression (typeof (System.Web.Profile.ProfileBase)),
+                               "Create");
+                       CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
+                               mref,
+                               new CodeExpression[] { new CodeVariableReferenceExpression ("username") }
+                       );
+                       CodeCastExpression cast = new CodeCastExpression ();
+                       cast.TargetType = new CodeTypeReference ("ProfileCommon");
+                       cast.Expression = minvoke;
+                       
+                       CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
+                       ret.Expression = cast;
+                       
+                       CodeMemberMethod method = new CodeMemberMethod ();
+                       method.Name = "GetProfile";
+                       method.ReturnType = new CodeTypeReference ("ProfileCommon");
+                       method.Parameters.Add (new CodeParameterDeclarationExpression("System.String", "username"));
+                       method.Statements.Add (ret);
+                       method.Attributes = MemberAttributes.Public;
+                       
+                       profileClass.Members.Add (method);
+               }
                
                void AddProfileClassProperty (ProfileSection ps, CodeTypeDeclaration profileClass, ProfilePropertySettings pset)
                {
@@ -484,10 +528,14 @@ namespace System.Web.Compilation
                }
                
                void BuildProfileClass (ProfileSection ps, string className, ProfilePropertySettingsCollection psc,
-                                       CodeNamespace ns, string baseClass, SortedList <string, string> groupProperties)
+                                       CodeNamespace ns, string baseClass, bool baseIsGlobal,
+                                       SortedList <string, string> groupProperties)
                {
                        CodeTypeDeclaration profileClass = new CodeTypeDeclaration (className);
-                       profileClass.BaseTypes.Add (new CodeTypeReference (baseClass));
+                       CodeTypeReference cref = new CodeTypeReference (baseClass);
+                       if (baseIsGlobal)
+                               cref.Options |= CodeTypeReferenceOptions.GlobalReference;
+                       profileClass.BaseTypes.Add (cref);
                        profileClass.TypeAttributes = TypeAttributes.Public;
                        ns.Types.Add (profileClass);
                        
@@ -496,11 +544,12 @@ namespace System.Web.Compilation
                        if (groupProperties != null && groupProperties.Count > 0)
                                foreach (KeyValuePair <string, string> group in groupProperties)
                                        AddProfileClassGroupProperty (group.Key, group.Value, profileClass);
+                       AddProfileClassGetProfileMethod (profileClass);
                }
 
                string MakeGroupName (string name)
                {
-                       return String.Format ("ProfileGroup{0}", name);
+                       return String.Concat ("ProfileGroup", name);
                }
                
                // FIXME: there should be some validation of syntactic correctness of the member/class name
@@ -521,7 +570,7 @@ namespace System.Web.Compilation
                        ns.Imports.Add (new CodeNamespaceImport ("System.Web.Profile"));
                        
                        RootProfilePropertySettingsCollection props = ps.PropertySettings;
-                       if (props == null || props.Count == 0)
+                       if (props == null)
                                return true;
 
                        SortedList<string, string> groupProperties = new SortedList<string, string> ();
@@ -530,13 +579,25 @@ namespace System.Web.Compilation
                                groupName = MakeGroupName (pgs.Name);
                                groupProperties.Add (groupName, pgs.Name);
                                BuildProfileClass (ps, groupName, pgs.PropertySettings, ns,
-                                                  "System.Web.Profile.ProfileGroupBase", null);
+                                                  "System.Web.Profile.ProfileGroupBase", true, null);
                        }
                        
                        string baseType = ps.Inherits;
                        if (String.IsNullOrEmpty (baseType))
                                baseType = "System.Web.Profile.ProfileBase";
-                       BuildProfileClass (ps, "ProfileCommon", props, ns, baseType, groupProperties);
+                       else {
+                               string[] parts = baseType.Split (new char[] {','});
+                               if (parts.Length > 1)
+                                       baseType = parts [0].Trim ();
+                       }
+                       
+                       bool baseIsGlobal;
+                       if (baseType.IndexOf ('.') != -1)
+                               baseIsGlobal = true;
+                       else
+                               baseIsGlobal = false;
+                       
+                       BuildProfileClass (ps, "ProfileCommon", props, ns, baseType, baseIsGlobal, groupProperties);
                        return true;
                }
 
@@ -548,17 +609,26 @@ namespace System.Web.Compilation
 //                     if (pb != null)
 //                             context.Profile = pb;
 //             }
+
+               public static bool HaveCustomProfile (ProfileSection ps)
+               {
+                       if (ps == null || !ps.Enabled)
+                               return false;
+                       if (!String.IsNullOrEmpty (ps.Inherits) || (ps.PropertySettings != null && ps.PropertySettings.Count > 0))
+                               return true;
+
+                       return false;
+               }
                
                public void Compile ()
                {
                        if (_alreadyCompiled)
                                return;
-                       _alreadyCompiled = false;
                        
                        string appCode = Path.Combine (HttpRuntime.AppDomainAppPath, "App_Code");
-                       ProfileSection ps = WebConfigurationManager.GetSection ("system.web/profile") as ProfileSection;
+                       ProfileSection ps = WebConfigurationManager.GetWebApplicationSection ("system.web/profile") as ProfileSection;
                        bool haveAppCodeDir = Directory.Exists (appCode);
-                       bool haveCustomProfile = ps != null ? ps.PropertySettings.Count > 0 : false;
+                       bool haveCustomProfile = HaveCustomProfile (ps);
                        
                        if (!haveAppCodeDir && !haveCustomProfile)
                                return;
@@ -575,18 +645,72 @@ namespace System.Web.Compilation
 
                        if (!haveCode)
                                return;
-
+                       
                        HttpRuntime.EnableAssemblyMapping (true);
-                       string bindir = BinDir;
-                       string[] binAssemblies = null;
-                       if (Directory.Exists (bindir))
-                               binAssemblies = Directory.GetFiles (bindir, "*.dll");
+                       string[] binAssemblies = HttpApplication.BinDirectoryAssemblies;
+                       
                        foreach (AppCodeAssembly aca in assemblies)
                                aca.Build (binAssemblies);
+                       _alreadyCompiled = true;
                        DefaultAppCodeAssemblyName = Path.GetFileNameWithoutExtension (defasm.OutputAssemblyName);
+
+                       RunAppInitialize ();
+                       
+                       if (haveCustomProfile && providerTypeName != null) {
+                               if (Type.GetType (providerTypeName, false) == null) {
+                                       foreach (Assembly asm in BuildManager.TopLevelAssemblies) {
+                                               if (asm == null)
+                                                       continue;
+                                               
+                                               if (asm.GetType (providerTypeName, false) != null)
+                                                       return;
+                                       }
+                               } else
+                                       return;
+
+                               if (HttpApplication.LoadTypeFromBin (providerTypeName) == null)
+                                       throw new HttpException (String.Format ("Profile provider type not found: {0}",
+                                                                               providerTypeName));
+                       }
                }
 
-               private bool CollectFiles (string dir, AppCodeAssembly aca)
+               // Documented (sort of...) briefly in:
+               //
+               //   http://quickstarts.asp.net/QuickStartv20/aspnet/doc/extensibility.aspx
+               //   http://msdn2.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx
+               void RunAppInitialize ()
+               {
+                       MethodInfo mi = null, tmi;
+                       Type[] types;
+                       
+                       foreach (Assembly asm in BuildManager.CodeAssemblies) {
+                               types = asm.GetExportedTypes ();
+                               if (types == null || types.Length == 0)
+                                       continue;
+
+                               foreach (Type type in types) {
+                                       tmi = type.GetMethod ("AppInitialize",
+                                                             BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
+                                                             null,
+                                                             Type.EmptyTypes,
+                                                             null);
+                                       if (tmi == null)
+                                               continue;
+
+                                       if (mi != null)
+                                               throw new HttpException ("The static AppInitialize method found in more than one type in the App_Code directory.");
+
+                                       mi = tmi;
+                               }
+                       }
+
+                       if (mi == null)
+                               return;
+
+                       mi.Invoke (null, null);
+               }
+               
+               bool CollectFiles (string dir, AppCodeAssembly aca)
                {
                        bool haveFiles = false;
                        
@@ -602,21 +726,12 @@ namespace System.Web.Compilation
                                                curaca = a;
                                                break;
                                        }
-                               CollectFiles (d, curaca);
+                               if (CollectFiles (d, curaca))
+                                       haveFiles = true;
                                curaca = aca;
                        }
                        return haveFiles;
                }
-
-               private string BinDir {
-                       get {
-                               if (_bindir != null)
-                                       return _bindir;
-                               AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
-                               _bindir = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
-                               return _bindir;
-                       }
-               }
        }
 }
 #endif