1 //
2 // System.Web.Compilation.AppCodeCompiler: A compiler for the App_Code folder
3 //
4 // Authors:
5 //   Marek Habersack (grendello@gmail.com)
6 //
7 // (C) 2006 Marek Habersack
8 //
9 
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 
31 using System;
32 using System.CodeDom;
33 using System.CodeDom.Compiler;
34 using System.Configuration;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Collections.Specialized;
38 using System.Globalization;
39 using System.IO;
40 using System.Reflection;
41 using System.Web;
42 using System.Web.Configuration;
43 using System.Web.Profile;
44 using System.Web.Util;
45 
46 namespace System.Web.Compilation
47 {
48 	class AssemblyPathResolver
49 	{
50 		static Dictionary <string, string> assemblyCache;
51 
AssemblyPathResolver()52 		static AssemblyPathResolver ()
53 		{
54 			assemblyCache = new Dictionary <string, string> ();
55 		}
56 
GetAssemblyPath(string assemblyName)57 		public static string GetAssemblyPath (string assemblyName)
58 		{
59 			lock (assemblyCache) {
60 				if (assemblyCache.ContainsKey (assemblyName))
61 					return assemblyCache [assemblyName];
62 
63 				Assembly asm = null;
64 				Exception error = null;
65 				if (assemblyName.IndexOf (',') != -1) {
66 					try {
67 						asm = Assembly.Load (assemblyName);
68 					} catch (Exception e) {
69 						error = e;
70 					}
71 				}
72 
73 				if (asm == null) {
74 					try {
75 						asm = Assembly.LoadWithPartialName (assemblyName);
76 					} catch (Exception e) {
77 						error = e;
78 					}
79 				}
80 
81 				if (asm == null)
82 					throw new HttpException (String.Format ("Unable to find assembly {0}", assemblyName), error);
83 
84 				string path = new Uri (asm.CodeBase).LocalPath;
85 				assemblyCache.Add (assemblyName, path);
86 				return path;
87 			}
88 		}
89 	}
90 
91 	internal class AppCodeAssembly
92 	{
93 		List<string> files;
94 		List<CodeCompileUnit> units;
95 
96 		string name;
97 		string path;
98 		bool validAssembly;
99 		string outputAssemblyName;
100 
101 		public string OutputAssemblyName
102 		{
103 			get {
104 				return outputAssemblyName;
105 			}
106 		}
107 
108 		public bool IsValid
109 		{
110 			get { return validAssembly; }
111 		}
112 
113 		public string SourcePath
114 		{
115 			get { return path; }
116 		}
117 
118 // temporary
119 		public string Name
120 		{
121 			get { return name; }
122 		}
123 
124 		public List<string> Files
125 		{
126 			get { return files; }
127 		}
128 // temporary
129 
AppCodeAssembly(string name, string path)130 		public AppCodeAssembly (string name, string path)
131 		{
132 			this.files = new List<string> ();
133 			this.units = new List<CodeCompileUnit> ();
134 			this.validAssembly = true;
135 			this.name = name;
136 			this.path = path;
137 		}
138 
AddFile(string path)139 		public void AddFile (string path)
140 		{
141 			files.Add (path);
142 		}
143 
AddUnit(CodeCompileUnit unit)144 		public void AddUnit (CodeCompileUnit unit)
145 		{
146 			units.Add (unit);
147 		}
148 
OnCreateTemporaryAssemblyFile(string path)149 		object OnCreateTemporaryAssemblyFile (string path)
150 		{
151 			FileStream f = new FileStream (path, FileMode.CreateNew);
152 			f.Close ();
153 			return path;
154 		}
155 
156 		// Build and add the assembly to the BuildManager's
157 		// CodeAssemblies collection
Build(string[] binAssemblies)158 		public void Build (string[] binAssemblies)
159 		{
160 			Type compilerProvider = null;
161 			CompilerInfo compilerInfo = null, cit;
162 			string extension, language, cpfile = null;
163 			List<string> knownfiles = new List<string>();
164 			List<string> unknownfiles = new List<string>();
165 
166 			// First make sure all the files are in the same
167 			// language
168 			bool known = false;
169 			foreach (string f in files) {
170 				known = true;
171 				language = null;
172 
173 				extension = Path.GetExtension (f);
174 				if (String.IsNullOrEmpty (extension) || !CodeDomProvider.IsDefinedExtension (extension))
175 					known = false;
176 				if (known) {
177 					language = CodeDomProvider.GetLanguageFromExtension(extension);
178 					if (!CodeDomProvider.IsDefinedLanguage (language))
179 						known = false;
180 				}
181 				if (!known || language == null) {
182 					unknownfiles.Add (f);
183 					continue;
184 				}
185 
186 				cit = CodeDomProvider.GetCompilerInfo (language);
187 				if (cit == null || !cit.IsCodeDomProviderTypeValid)
188 					continue;
189 				if (compilerProvider == null) {
190 					cpfile = f;
191 					compilerProvider = cit.CodeDomProviderType;
192 					compilerInfo = cit;
193 				} else if (compilerProvider != cit.CodeDomProviderType)
194 					throw new HttpException (
195 						String.Format (
196 							"Files {0} and {1} are in different languages - they cannot be compiled into the same assembly",
197 							Path.GetFileName (cpfile),
198 							Path.GetFileName (f)));
199 				knownfiles.Add (f);
200 			}
201 
202 			CodeDomProvider provider = null;
203 			CompilationSection compilationSection = WebConfigurationManager.GetWebApplicationSection ("system.web/compilation") as CompilationSection;
204 			if (compilerInfo == null) {
205 				if (!CodeDomProvider.IsDefinedLanguage (compilationSection.DefaultLanguage))
206 					throw new HttpException ("Failed to retrieve default source language");
207 				compilerInfo = CodeDomProvider.GetCompilerInfo (compilationSection.DefaultLanguage);
208 				if (compilerInfo == null || !compilerInfo.IsCodeDomProviderTypeValid)
209 					throw new HttpException ("Internal error while initializing application");
210 			}
211 
212 			provider = compilerInfo.CreateProvider ();
213 			if (provider == null)
214 				throw new HttpException ("A code provider error occurred while initializing application.");
215 
216 			AssemblyBuilder abuilder = new AssemblyBuilder (provider);
217 			foreach (string file in knownfiles)
218 				abuilder.AddCodeFile (file);
219 			foreach (CodeCompileUnit unit in units)
220 				abuilder.AddCodeCompileUnit (unit);
221 
222 			BuildProvider bprovider;
223 			CompilerParameters parameters = compilerInfo.CreateDefaultCompilerParameters ();
224 			parameters.IncludeDebugInformation = compilationSection.Debug;
225 
226 			if (binAssemblies != null && binAssemblies.Length > 0) {
227 				StringCollection parmRefAsm = parameters.ReferencedAssemblies;
228 				foreach (string binAsm in binAssemblies) {
229 					if (parmRefAsm.Contains (binAsm))
230 						continue;
231 
232 					parmRefAsm.Add (binAsm);
233 				}
234 			}
235 
236 			if (compilationSection != null) {
237 				foreach (AssemblyInfo ai in compilationSection.Assemblies)
238 					if (ai.Assembly != "*") {
239 						try {
240 							parameters.ReferencedAssemblies.Add (
241 								AssemblyPathResolver.GetAssemblyPath (ai.Assembly));
242 						} catch (Exception ex) {
243 							throw new HttpException (
244 								String.Format ("Could not find assembly {0}.", ai.Assembly),
245 								ex);
246 						}
247 					}
248 
249 				BuildProviderCollection buildProviders = compilationSection.BuildProviders;
250 
251 				foreach (string file in unknownfiles) {
252 					bprovider = GetBuildProviderFor (file, buildProviders);
253 					if (bprovider == null)
254 						continue;
255 					bprovider.GenerateCode (abuilder);
256 				}
257 			}
258 
259 			if (knownfiles.Count == 0 && unknownfiles.Count == 0 && units.Count == 0)
260 				return;
261 
262 			outputAssemblyName = (string)FileUtils.CreateTemporaryFile (
263 				AppDomain.CurrentDomain.SetupInformation.DynamicBase,
264 				name, "dll", OnCreateTemporaryAssemblyFile);
265 			parameters.OutputAssembly = outputAssemblyName;
266 			foreach (Assembly a in BuildManager.TopLevelAssemblies)
267 				parameters.ReferencedAssemblies.Add (a.Location);
268 			CompilerResults results = abuilder.BuildAssembly (parameters);
269 			if (results == null)
270 				return;
271 
272 			if (results.NativeCompilerReturnValue == 0) {
273 				BuildManager.CodeAssemblies.Add (results.CompiledAssembly);
274 				BuildManager.TopLevelAssemblies.Add (results.CompiledAssembly);
275 				HttpRuntime.WritePreservationFile (results.CompiledAssembly, name);
276 			} else {
277 				if (HttpContext.Current.IsCustomErrorEnabled)
278 					throw new HttpException ("An error occurred while initializing application.");
279 				throw new CompilationException (null, results.Errors, null);
280 			}
281 		}
282 
PhysicalToVirtual(string file)283 		VirtualPath PhysicalToVirtual (string file)
284 		{
285 			return new VirtualPath (file.Replace (HttpRuntime.AppDomainAppPath, "~/").Replace (Path.DirectorySeparatorChar, '/'));
286 		}
287 
GetBuildProviderFor(string file, BuildProviderCollection buildProviders)288 		BuildProvider GetBuildProviderFor (string file, BuildProviderCollection buildProviders)
289 		{
290 			if (file == null || file.Length == 0 || buildProviders == null || buildProviders.Count == 0)
291 				return null;
292 
293 			BuildProvider ret = buildProviders.GetProviderInstanceForExtension (Path.GetExtension (file));
294 			if (ret != null && IsCorrectBuilderType (ret)) {
295 				ret.SetVirtualPath (PhysicalToVirtual (file));
296 				return ret;
297 			}
298 
299 			return null;
300 		}
301 
IsCorrectBuilderType(BuildProvider bp)302 		bool IsCorrectBuilderType (BuildProvider bp)
303 		{
304 			if (bp == null)
305 				return false;
306 			Type type;
307 			object[] attrs;
308 
309 			type = bp.GetType ();
310 			attrs = type.GetCustomAttributes (true);
311 			if (attrs == null)
312 				return false;
313 
314 			BuildProviderAppliesToAttribute bpAppliesTo;
315 			bool attributeFound = false;
316 			foreach (object attr in attrs) {
317 				bpAppliesTo = attr as BuildProviderAppliesToAttribute;
318 				if (bpAppliesTo == null)
319 					continue;
320 				attributeFound = true;
321 				if ((bpAppliesTo.AppliesTo & BuildProviderAppliesTo.All) == BuildProviderAppliesTo.All ||
322 				    (bpAppliesTo.AppliesTo & BuildProviderAppliesTo.Code) == BuildProviderAppliesTo.Code)
323 					return true;
324 			}
325 
326 			if (attributeFound)
327 				return false;
328 			return true;
329 		}
330 
331 	}
332 
333 	internal class AppCodeCompiler
334 	{
335 		static bool _alreadyCompiled;
336 		internal static string DefaultAppCodeAssemblyName;
337 
338 		// A dictionary that contains an entry per an assembly that will
339 		// be produced by compiling App_Code. There's one main assembly
340 		// and an optional number of assemblies as defined by the
341 		// codeSubDirectories sub-element of the compilation element in
342 		// the system.web section of the app's config file.
343 		// Each entry's value is an AppCodeAssembly instance.
344 		//
345 		// Assemblies are named as follows:
346 		//
347 		//  1. main assembly: App_Code.{HASH}
348 		//  2. subdir assemblies: App_SubCode_{DirName}.{HASH}
349 		//
350 		// If any of the assemblies contains files that would be
351 		// compiled with different compilers, a System.Web.HttpException
352 		// is thrown.
353 		//
354 		// Files for which there is no explicit builder are ignored
355 		// silently
356 		//
357 		// Files for which exist BuildProviders but which have no
358 		// unambiguous language assigned to them (e.g. .wsdl files), are
359 		// built using the default website compiler.
360 		List<AppCodeAssembly> assemblies;
361 		string providerTypeName = null;
362 
AppCodeCompiler()363 		public AppCodeCompiler ()
364 		{
365 			assemblies = new List<AppCodeAssembly>();
366 		}
367 
ProcessAppCodeDir(string appCode, AppCodeAssembly defasm)368 		bool ProcessAppCodeDir (string appCode, AppCodeAssembly defasm)
369 		{
370 			// First process the codeSubDirectories
371 			CompilationSection cs = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
372 
373 			if (cs != null) {
374 				string aname;
375 				for (int i = 0; i < cs.CodeSubDirectories.Count; i++) {
376 					aname = String.Concat ("App_SubCode_", cs.CodeSubDirectories[i].DirectoryName);
377 					assemblies.Add (new AppCodeAssembly (
378 								aname,
379 								Path.Combine (appCode, cs.CodeSubDirectories[i].DirectoryName)));
380 				}
381 			}
382 
383 			return CollectFiles (appCode, defasm);
384 		}
385 
GetProfilePropertyType(string type)386 		CodeTypeReference GetProfilePropertyType (string type)
387 		{
388 			if (String.IsNullOrEmpty (type))
389 				throw new ArgumentException ("String size cannot be 0", "type");
390 			return new CodeTypeReference (type);
391 		}
392 
FindProviderTypeName(ProfileSection ps, string providerName)393 		string FindProviderTypeName (ProfileSection ps, string providerName)
394 		{
395 			if (ps.Providers == null || ps.Providers.Count == 0)
396 				return null;
397 
398 			ProviderSettings pset = ps.Providers [providerName];
399 			if (pset == null)
400 				return null;
401 			return pset.Type;
402 		}
403 
GetProfileProviderAttribute(ProfileSection ps, CodeAttributeDeclarationCollection collection, string providerName)404 		void GetProfileProviderAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
405 						  string providerName)
406 		{
407 			if (String.IsNullOrEmpty (providerName))
408 				providerTypeName = FindProviderTypeName (ps, ps.DefaultProvider);
409 			else
410 				providerTypeName = FindProviderTypeName (ps, providerName);
411 			if (providerTypeName == null)
412 				throw new HttpException (String.Format ("Profile provider type not defined: {0}",
413 									providerName));
414 
415 			collection.Add (
416 				new CodeAttributeDeclaration (
417 					"ProfileProvider",
418 					new CodeAttributeArgument (
419 						new CodePrimitiveExpression (providerTypeName)
420 					)
421 				)
422 			);
423 		}
424 
GetProfileSettingsSerializeAsAttribute(ProfileSection ps, CodeAttributeDeclarationCollection collection, SerializationMode mode)425 		void GetProfileSettingsSerializeAsAttribute (ProfileSection ps, CodeAttributeDeclarationCollection collection,
426 							     SerializationMode mode)
427 		{
428 			string parameter = String.Concat ("SettingsSerializeAs.", mode.ToString ());
429 			collection.Add (
430 				new CodeAttributeDeclaration (
431 					"SettingsSerializeAs",
432 					new CodeAttributeArgument (
433 						new CodeSnippetExpression (parameter)
434 					)
435 				)
436 			);
437 
438 		}
439 
AddProfileClassGetProfileMethod(CodeTypeDeclaration profileClass)440 		void AddProfileClassGetProfileMethod (CodeTypeDeclaration profileClass)
441 		{
442 			CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
443 				new CodeTypeReferenceExpression (typeof (System.Web.Profile.ProfileBase)),
444 				"Create");
445 			CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
446 				mref,
447 				new CodeExpression[] { new CodeVariableReferenceExpression ("username") }
448 			);
449 			CodeCastExpression cast = new CodeCastExpression ();
450 			cast.TargetType = new CodeTypeReference ("ProfileCommon");
451 			cast.Expression = minvoke;
452 
453 			CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
454 			ret.Expression = cast;
455 
456 			CodeMemberMethod method = new CodeMemberMethod ();
457 			method.Name = "GetProfile";
458 			method.ReturnType = new CodeTypeReference ("ProfileCommon");
459 			method.Parameters.Add (new CodeParameterDeclarationExpression("System.String", "username"));
460 			method.Statements.Add (ret);
461 			method.Attributes = MemberAttributes.Public;
462 
463 			profileClass.Members.Add (method);
464 		}
465 
AddProfileClassProperty(ProfileSection ps, CodeTypeDeclaration profileClass, ProfilePropertySettings pset)466 		void AddProfileClassProperty (ProfileSection ps, CodeTypeDeclaration profileClass, ProfilePropertySettings pset)
467 		{
468 			string name = pset.Name;
469 			if (String.IsNullOrEmpty (name))
470 				throw new HttpException ("Profile property 'Name' attribute cannot be null.");
471 			CodeMemberProperty property = new CodeMemberProperty ();
472 			string typeName = pset.Type;
473 			if (typeName == "string")
474 				typeName = "System.String";
475 			property.Name = name;
476 			property.Type = GetProfilePropertyType (typeName);
477 			property.Attributes = MemberAttributes.Public;
478 
479 			CodeAttributeDeclarationCollection collection = new CodeAttributeDeclarationCollection();
480 			GetProfileProviderAttribute (ps, collection, pset.Provider);
481 			GetProfileSettingsSerializeAsAttribute (ps, collection, pset.SerializeAs);
482 
483 			property.CustomAttributes = collection;
484 			CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
485 			CodeCastExpression cast = new CodeCastExpression ();
486 			ret.Expression = cast;
487 
488 			CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
489 				new CodeThisReferenceExpression (),
490 				"GetPropertyValue");
491 			CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
492 				mref,
493 				new CodeExpression[] { new CodePrimitiveExpression (name) }
494 			);
495 			cast.TargetType = new CodeTypeReference (typeName);
496 			cast.Expression = minvoke;
497 			property.GetStatements.Add (ret);
498 
499 			if (!pset.ReadOnly) {
500 				mref = new CodeMethodReferenceExpression (
501 					new CodeThisReferenceExpression (),
502 					"SetPropertyValue");
503 				minvoke = new CodeMethodInvokeExpression (
504 					mref,
505 					new CodeExpression[] { new CodePrimitiveExpression (name), new CodeSnippetExpression ("value") }
506 				);
507 				property.SetStatements.Add (minvoke);
508 			}
509 
510 
511 			profileClass.Members.Add (property);
512 		}
513 
AddProfileClassGroupProperty(string groupName, string memberName, CodeTypeDeclaration profileClass)514 		void AddProfileClassGroupProperty (string groupName, string memberName, CodeTypeDeclaration profileClass)
515 		{
516 			CodeMemberProperty property = new CodeMemberProperty ();
517 			property.Name = memberName;
518 			property.Type = new CodeTypeReference (groupName);
519 			property.Attributes = MemberAttributes.Public;
520 
521 			CodeMethodReturnStatement ret = new CodeMethodReturnStatement ();
522 			CodeCastExpression cast = new CodeCastExpression ();
523 			ret.Expression = cast;
524 
525 			CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
526 				new CodeThisReferenceExpression (),
527 				"GetProfileGroup");
528 			CodeMethodInvokeExpression minvoke = new CodeMethodInvokeExpression (
529 				mref,
530 				new CodeExpression[] { new CodePrimitiveExpression (memberName) }
531 			);
532 			cast.TargetType = new CodeTypeReference (groupName);
533 			cast.Expression = minvoke;
534 			property.GetStatements.Add (ret);
535 
536 			profileClass.Members.Add (property);
537 		}
538 
BuildProfileClass(ProfileSection ps, string className, ProfilePropertySettingsCollection psc, CodeNamespace ns, string baseClass, bool baseIsGlobal, SortedList <string, string> groupProperties)539 		void BuildProfileClass (ProfileSection ps, string className, ProfilePropertySettingsCollection psc,
540 					CodeNamespace ns, string baseClass, bool baseIsGlobal,
541 					SortedList <string, string> groupProperties)
542 		{
543 			CodeTypeDeclaration profileClass = new CodeTypeDeclaration (className);
544 			CodeTypeReference cref = new CodeTypeReference (baseClass);
545 			if (baseIsGlobal)
546 				cref.Options |= CodeTypeReferenceOptions.GlobalReference;
547 			profileClass.BaseTypes.Add (cref);
548 			profileClass.TypeAttributes = TypeAttributes.Public;
549 			ns.Types.Add (profileClass);
550 
551 			foreach (ProfilePropertySettings pset in psc)
552 				AddProfileClassProperty (ps, profileClass, pset);
553 			if (groupProperties != null && groupProperties.Count > 0)
554 				foreach (KeyValuePair <string, string> group in groupProperties)
555 					AddProfileClassGroupProperty (group.Key, group.Value, profileClass);
556 			AddProfileClassGetProfileMethod (profileClass);
557 		}
558 
MakeGroupName(string name)559 		string MakeGroupName (string name)
560 		{
561 			return String.Concat ("ProfileGroup", name);
562 		}
563 
564 		// FIXME: there should be some validation of syntactic correctness of the member/class name
565 		// for the groups/properties. For now it's left to the compiler to report errors.
566 		//
567 		// CodeGenerator.IsValidLanguageIndependentIdentifier (id) - use that
568 		//
ProcessCustomProfile(ProfileSection ps, AppCodeAssembly defasm)569 		bool ProcessCustomProfile (ProfileSection ps, AppCodeAssembly defasm)
570 		{
571 			CodeCompileUnit unit = new CodeCompileUnit ();
572 			CodeNamespace ns = new CodeNamespace (null);
573 			unit.Namespaces.Add (ns);
574 			defasm.AddUnit (unit);
575 
576 			ns.Imports.Add (new CodeNamespaceImport ("System"));
577 			ns.Imports.Add (new CodeNamespaceImport ("System.Configuration"));
578 			ns.Imports.Add (new CodeNamespaceImport ("System.Web"));
579 			ns.Imports.Add (new CodeNamespaceImport ("System.Web.Profile"));
580 
581 			RootProfilePropertySettingsCollection props = ps.PropertySettings;
582 			if (props == null)
583 				return true;
584 
585 			SortedList<string, string> groupProperties = new SortedList<string, string> ();
586 			string groupName;
587 			foreach (ProfileGroupSettings pgs in props.GroupSettings) {
588 				groupName = MakeGroupName (pgs.Name);
589 				groupProperties.Add (groupName, pgs.Name);
590 				BuildProfileClass (ps, groupName, pgs.PropertySettings, ns,
591 						   "System.Web.Profile.ProfileGroupBase", true, null);
592 			}
593 
594 			string baseType = ps.Inherits;
595 			if (String.IsNullOrEmpty (baseType))
596 				baseType = "System.Web.Profile.ProfileBase";
597 			else {
598 				string[] parts = baseType.Split (new char[] {','});
599 				if (parts.Length > 1)
600 					baseType = parts [0].Trim ();
601 			}
602 
603 			bool baseIsGlobal;
604 			if (baseType.IndexOf ('.') != -1)
605 				baseIsGlobal = true;
606 			else
607 				baseIsGlobal = false;
608 
609 			BuildProfileClass (ps, "ProfileCommon", props, ns, baseType, baseIsGlobal, groupProperties);
610 			return true;
611 		}
612 
613 //		void PutCustomProfileInContext (HttpContext context, string assemblyName)
614 //		{
615 //			Type type = Type.GetType (String.Format ("ProfileCommon, {0}",
616 //								 Path.GetFileNameWithoutExtension (assemblyName)));
617 //			ProfileBase pb = Activator.CreateInstance (type) as ProfileBase;
618 //			if (pb != null)
619 //				context.Profile = pb;
620 //		}
621 
HaveCustomProfile(ProfileSection ps)622 		public static bool HaveCustomProfile (ProfileSection ps)
623 		{
624 			if (ps == null || !ps.Enabled)
625 				return false;
626 
627 			RootProfilePropertySettingsCollection props = ps.PropertySettings;
628 			ProfileGroupSettingsCollection groups = props != null ? props.GroupSettings : null;
629 
630 			if (!String.IsNullOrEmpty (ps.Inherits) || (props != null && props.Count > 0) || (groups != null && groups.Count > 0))
631 				return true;
632 
633 			return false;
634 		}
635 
Compile()636 		public void Compile ()
637 		{
638 			if (_alreadyCompiled)
639 				return;
640 
641 			string appCode = Path.Combine (HttpRuntime.AppDomainAppPath, "App_Code");
642 			ProfileSection ps = WebConfigurationManager.GetWebApplicationSection ("system.web/profile") as ProfileSection;
643 			bool haveAppCodeDir = Directory.Exists (appCode);
644 			bool haveCustomProfile = HaveCustomProfile (ps);
645 
646 			if (!haveAppCodeDir && !haveCustomProfile)
647 				return;
648 
649 			AppCodeAssembly defasm = new AppCodeAssembly ("App_Code", appCode);
650 			assemblies.Add (defasm);
651 
652 			bool haveCode = false;
653 			if (haveAppCodeDir)
654 				haveCode = ProcessAppCodeDir (appCode, defasm);
655 			if (haveCustomProfile)
656 				if (ProcessCustomProfile (ps, defasm))
657 					haveCode = true;
658 
659 			if (!haveCode)
660 				return;
661 
662 			HttpRuntime.EnableAssemblyMapping (true);
663 			string[] binAssemblies = HttpApplication.BinDirectoryAssemblies;
664 
665 			foreach (AppCodeAssembly aca in assemblies)
666 				aca.Build (binAssemblies);
667 			_alreadyCompiled = true;
668 			DefaultAppCodeAssemblyName = Path.GetFileNameWithoutExtension (defasm.OutputAssemblyName);
669 
670 			RunAppInitialize ();
671 
672 			if (haveCustomProfile && providerTypeName != null) {
673 				if (Type.GetType (providerTypeName, false) == null) {
674 					foreach (Assembly asm in BuildManager.TopLevelAssemblies) {
675 						if (asm == null)
676 							continue;
677 
678 						if (asm.GetType (providerTypeName, false) != null)
679 							return;
680 					}
681 				} else
682 					return;
683 
684 				Exception noTypeException = null;
685 				Type ptype = null;
686 
687 				try {
688 					ptype = HttpApplication.LoadTypeFromBin (providerTypeName);
689 				} catch (Exception ex) {
690 					noTypeException = ex;
691 				}
692 
693 				if (ptype == null)
694 					throw new HttpException (String.Format ("Profile provider type not found: {0}", providerTypeName), noTypeException);
695 			}
696 		}
697 
698 		// Documented (sort of...) briefly in:
699 		//
700 		//   http://quickstarts.asp.net/QuickStartv20/aspnet/doc/extensibility.aspx
701 		//   http://msdn2.microsoft.com/en-us/library/system.web.hosting.virtualpathprovider.aspx
RunAppInitialize()702 		void RunAppInitialize ()
703 		{
704 			MethodInfo mi = null, tmi;
705 			Type[] types;
706 
707 			foreach (Assembly asm in BuildManager.CodeAssemblies) {
708 				types = asm.GetExportedTypes ();
709 				if (types == null || types.Length == 0)
710 					continue;
711 
712 				foreach (Type type in types) {
713 					tmi = type.GetMethod ("AppInitialize",
714 							      BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,
715 							      null,
716 							      Type.EmptyTypes,
717 							      null);
718 					if (tmi == null)
719 						continue;
720 
721 					if (mi != null)
722 						throw new HttpException ("The static AppInitialize method found in more than one type in the App_Code directory.");
723 
724 					mi = tmi;
725 				}
726 			}
727 
728 			if (mi == null)
729 				return;
730 
731 			mi.Invoke (null, null);
732 		}
733 
CollectFiles(string dir, AppCodeAssembly aca)734 		bool CollectFiles (string dir, AppCodeAssembly aca)
735 		{
736 			bool haveFiles = false;
737 
738 			AppCodeAssembly curaca = aca;
739 			foreach (string f in Directory.GetFiles (dir)) {
740 				aca.AddFile (f);
741 				haveFiles = true;
742 			}
743 
744 			foreach (string d in Directory.GetDirectories (dir)) {
745 				foreach (AppCodeAssembly a in assemblies)
746 					if (a.SourcePath == d) {
747 						curaca = a;
748 						break;
749 					}
750 				if (CollectFiles (d, curaca))
751 					haveFiles = true;
752 				curaca = aca;
753 			}
754 			return haveFiles;
755 		}
756 	}
757 }
758 
759