2 // System.Web.Compilation.BaseCompiler
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (c) Copyright 2002,2003 Ximian, Inc (http://www.ximian.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
33 using System.CodeDom.Compiler;
34 using System.Collections;
35 using System.Reflection;
38 using System.Web.Configuration;
41 namespace System.Web.Compilation
43 abstract class BaseCompiler
46 static BindingFlags replaceableFlags = BindingFlags.Public | BindingFlags.NonPublic |
47 BindingFlags.Instance;
50 TemplateParser parser;
51 CodeDomProvider provider;
52 ICodeCompiler compiler;
55 CompilerParameters compilerParameters;
57 bool isRebuilding = false;
58 protected Hashtable partialNameOverride = new Hashtable();
60 protected CodeTypeDeclaration mainClass;
61 protected CodeTypeReferenceExpression mainClassExpr;
62 protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
64 protected BaseCompiler (TemplateParser parser)
66 compilerParameters = new CompilerParameters ();
72 unit = new CodeCompileUnit ();
74 if (parser.IsPartial) {
76 string classtype = parser.PartialClassName;
78 if (classtype.Contains (".")) {
79 int dot = classtype.LastIndexOf (".");
80 ns = classtype.Substring (0, dot);
81 classtype = classtype.Substring (dot + 1);
84 mainNS = new CodeNamespace (ns);
85 mainClass = new CodeTypeDeclaration (classtype);
86 mainClass.IsPartial = true;
87 mainClassExpr = new CodeTypeReferenceExpression (parser.PartialClassName);
90 mainNS = new CodeNamespace ("ASP");
91 mainClass = new CodeTypeDeclaration (parser.ClassName);
92 CodeTypeReference baseTypeRef = new CodeTypeReference (parser.BaseType.FullName);
94 if (parser.BaseTypeIsGlobal)
95 baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
97 mainClass.BaseTypes.Add (baseTypeRef);
98 mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
102 unit.Namespaces.Add (mainNS);
103 mainClass.TypeAttributes = TypeAttributes.Public;
104 mainNS.Types.Add (mainClass);
106 foreach (object o in parser.Imports) {
108 mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
111 if (parser.Assemblies != null) {
112 foreach (object o in parser.Assemblies) {
114 unit.ReferencedAssemblies.Add ((string) o);
119 ArrayList al = WebConfigurationManager.ExtraAssemblies;
120 if (al != null && al.Count > 0) {
121 foreach (object o in al)
123 unit.ReferencedAssemblies.Add ((string) o);
126 IList list = BuildManager.CodeAssemblies;
127 if (list != null && list.Count > 0) {
128 foreach (object o in list)
130 unit.ReferencedAssemblies.Add ((string) o);
133 // Late-bound generators specifics (as for MonoBASIC/VB.NET)
134 unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
135 unit.UserData["AllowLateBound"] = !parser.StrictOn;
138 AddClassAttributes ();
139 CreateStaticFields ();
140 AddApplicationAndSessionObjects ();
143 CreateConstructor (null, null);
147 internal CodeDomProvider Provider {
148 get { return provider; }
151 internal CodeCompileUnit CompileUnit {
155 protected virtual void CreateStaticFields ()
157 CodeMemberField fld = new CodeMemberField (typeof (bool), "__initialized");
158 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
159 fld.InitExpression = new CodePrimitiveExpression (false);
160 mainClass.Members.Add (fld);
163 protected virtual void CreateConstructor (CodeStatementCollection localVars,
164 CodeStatementCollection trueStmt)
166 CodeConstructor ctor = new CodeConstructor ();
167 ctor.Attributes = MemberAttributes.Public;
168 mainClass.Members.Add (ctor);
170 if (localVars != null)
171 ctor.Statements.AddRange (localVars);
173 CodeTypeReferenceExpression r;
175 if (parser.IsPartial)
176 r = new CodeTypeReferenceExpression (mainClass.Name);
179 r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
180 CodeFieldReferenceExpression initialized;
181 initialized = new CodeFieldReferenceExpression (r, "__initialized");
183 CodeBinaryOperatorExpression bin;
184 bin = new CodeBinaryOperatorExpression (initialized,
185 CodeBinaryOperatorType.ValueEquality,
186 new CodePrimitiveExpression (false));
188 CodeAssignStatement assign = new CodeAssignStatement (initialized,
189 new CodePrimitiveExpression (true));
191 CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
192 if (trueStmt != null)
193 cond.TrueStatements.AddRange (trueStmt);
195 ctor.Statements.Add (cond);
200 if (parser.Scripts == null || parser.Scripts.Count == 0)
203 foreach (object o in parser.Scripts) {
205 mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
209 protected internal virtual void CreateMethods ()
213 protected virtual void AddInterfaces ()
215 if (parser.Interfaces == null)
218 foreach (object o in parser.Interfaces) {
220 mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
224 protected virtual void AddClassAttributes ()
228 protected virtual void AddApplicationAndSessionObjects ()
232 /* Utility methods for <object> stuff */
233 protected void CreateApplicationOrSessionPropertyForObject (Type type,
238 /* if isApplication this generates (the 'cachedapp' field is created earlier):
239 private MyNS.MyClass app {
241 if ((this.cachedapp == null)) {
242 this.cachedapp = ((MyNS.MyClass)
243 (this.Application.StaticObjects.GetObject("app")));
245 return this.cachedapp;
249 else, this is for Session:
250 private MyNS.MyClass ses {
252 return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
258 CodeExpression result = null;
260 CodeMemberProperty prop = new CodeMemberProperty ();
261 prop.Type = new CodeTypeReference (type);
262 prop.Name = propName;
264 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
266 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
268 CodePropertyReferenceExpression p1;
270 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
272 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
274 CodePropertyReferenceExpression p2;
275 p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
277 CodeMethodReferenceExpression getobject;
278 getobject = new CodeMethodReferenceExpression (p2, "GetObject");
280 CodeMethodInvokeExpression invoker;
281 invoker = new CodeMethodInvokeExpression (getobject,
282 new CodePrimitiveExpression (propName));
284 CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
287 CodeFieldReferenceExpression field;
288 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
290 CodeConditionStatement stmt = new CodeConditionStatement();
291 stmt.Condition = new CodeBinaryOperatorExpression (field,
292 CodeBinaryOperatorType.IdentityEquality,
293 new CodePrimitiveExpression (null));
295 CodeAssignStatement assign = new CodeAssignStatement ();
298 stmt.TrueStatements.Add (assign);
299 prop.GetStatements.Add (stmt);
305 prop.GetStatements.Add (new CodeMethodReturnStatement (result));
306 mainClass.Members.Add (prop);
309 protected string CreateFieldForObject (Type type, string name)
311 string fieldName = "cached" + name;
312 CodeMemberField f = new CodeMemberField (type, fieldName);
313 f.Attributes = MemberAttributes.Private;
314 mainClass.Members.Add (f);
318 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
320 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
321 CodeMemberProperty prop = new CodeMemberProperty ();
322 prop.Type = new CodeTypeReference (type);
323 prop.Name = propName;
325 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
327 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
329 CodeConditionStatement stmt = new CodeConditionStatement();
330 stmt.Condition = new CodeBinaryOperatorExpression (field,
331 CodeBinaryOperatorType.IdentityEquality,
332 new CodePrimitiveExpression (null));
334 CodeObjectCreateExpression create = new CodeObjectCreateExpression (prop.Type);
335 stmt.TrueStatements.Add (new CodeAssignStatement (field, create));
336 prop.GetStatements.Add (stmt);
337 prop.GetStatements.Add (new CodeMethodReturnStatement (field));
339 mainClass.Members.Add (prop);
343 void CheckCompilerErrors (CompilerResults results)
345 if (results.NativeCompilerReturnValue == 0)
348 StringWriter writer = new StringWriter();
349 provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
350 throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
353 protected string DynamicDir ()
355 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
358 [MonoTODO ("find out how to extract the warningLevel and compilerOptions in the <system.codedom> case")]
359 public virtual Type GetCompiledType ()
361 Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
366 string lang = parser.Language;
368 CompilationSection config = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
369 Compiler comp = config.Compilers[lang];
371 string compilerOptions = "";
372 int warningLevel = 0;
375 CompilerInfo info = CodeDomProvider.GetCompilerInfo (lang);
376 if (info != null && info.IsCodeDomProviderTypeValid)
377 provider = info.CreateProvider ();
379 // XXX there's no way to get
380 // warningLevel or compilerOptions out
381 // of the provider.. they're in the
382 // configuration section, though.
385 Type t = Type.GetType (comp.Type, true);
386 provider = Activator.CreateInstance (t) as CodeDomProvider;
388 compilerOptions = comp.CompilerOptions;
389 warningLevel = comp.WarningLevel;
393 CompilationConfiguration config;
395 config = CompilationConfiguration.GetInstance (parser.Context);
396 provider = config.GetProvider (lang);
398 string compilerOptions = config.GetCompilerOptions (lang);
399 int warningLevel = config.GetWarningLevel (lang);
401 if (provider == null)
402 throw new HttpException ("Configuration error. Language not supported: " +
405 compiler = provider.CreateCompiler ();
407 compilerParameters.IncludeDebugInformation = parser.Debug;
408 compilerParameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
410 compilerParameters.WarningLevel = warningLevel;
411 bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
413 string tempdir = config.TempDirectory;
414 if (tempdir == null || tempdir == "")
415 tempdir = DynamicDir ();
417 TempFileCollection tempcoll = new TempFileCollection (tempdir, keepFiles);
418 compilerParameters.TempFiles = tempcoll;
419 string dllfilename = Path.GetFileName (tempcoll.AddExtension ("dll", true));
420 compilerParameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
422 CompilerResults results = CachingCompiler.Compile (this);
423 CheckCompilerErrors (results);
424 Assembly assembly = results.CompiledAssembly;
425 if (assembly == null) {
426 if (!File.Exists (compilerParameters.OutputAssembly)) {
427 results.TempFiles.Delete ();
428 throw new CompilationException (parser.InputFile, results.Errors,
429 "No assembly returned after compilation!?");
432 assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
435 results.TempFiles.Delete ();
436 Type mainClassType = assembly.GetType (mainClassExpr.Type.BaseType, true);
439 if (parser.IsPartial) {
440 // With the partial classes, we need to make sure we
441 // don't have any methods that should have not been
442 // created (because they are accessible from the base
443 // types). We cannot do this normally because the
444 // codebehind file is actually a partial class and we
445 // have no way of identifying the partial class' base
447 if (!isRebuilding && CheckPartialBaseType (mainClassType)) {
449 parser.RootBuilder.ResetState ();
450 return GetCompiledType ();
455 return mainClassType;
459 internal bool IsRebuildingPartial
461 get { return isRebuilding; }
464 internal bool CheckPartialBaseType (Type type)
466 // Get the base type. If we don't have any (bad thing), we
467 // don't need to replace ourselves. Also check for the
468 // core file, since that won't have any either.
469 Type baseType = type.BaseType;
470 if (baseType == null || baseType == typeof(System.Web.UI.Page))
473 bool rebuild = false;
475 if (CheckPartialBaseFields (type, baseType))
478 if (CheckPartialBaseProperties (type, baseType))
484 internal bool CheckPartialBaseFields (Type type, Type baseType)
486 bool rebuild = false;
488 foreach (FieldInfo baseInfo in baseType.GetFields (replaceableFlags)) {
489 if (baseInfo.IsPrivate)
492 FieldInfo typeInfo = type.GetField (baseInfo.Name, replaceableFlags);
494 if (typeInfo != null && typeInfo.DeclaringType == type) {
495 partialNameOverride [typeInfo.Name] = true;
503 internal bool CheckPartialBaseProperties (Type type, Type baseType)
505 bool rebuild = false;
507 foreach (PropertyInfo baseInfo in baseType.GetProperties ()) {
508 PropertyInfo typeInfo = type.GetProperty (baseInfo.Name);
510 if (typeInfo != null && typeInfo.DeclaringType == type) {
511 partialNameOverride [typeInfo.Name] = true;
520 internal CompilerParameters CompilerParameters {
521 get { return compilerParameters; }
524 internal CodeCompileUnit Unit {
528 internal virtual ICodeCompiler Compiler {
529 get { return compiler; }
532 internal TemplateParser Parser {
533 get { return parser; }