Mark AutoHelpers as obsolete in generated code and a fix for bug #77069
[mono.git] / mcs / class / System.Web / System.Web.Compilation / BaseCompiler.cs
1 //
2 // System.Web.Compilation.BaseCompiler
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (c) Copyright 2002,2003 Ximian, Inc (http://www.ximian.com)
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.Collections;
35 using System.Reflection;
36 using System.Text;
37 using System.Web.UI;
38 using System.Web.Configuration;
39 using System.IO;
40
41 namespace System.Web.Compilation
42 {
43         abstract class BaseCompiler
44         {
45 #if NET_2_0
46                 static BindingFlags replaceableFlags = BindingFlags.Public | BindingFlags.NonPublic |
47                                                   BindingFlags.Instance;
48 #endif
49
50                 TemplateParser parser;
51                 CodeDomProvider provider;
52                 ICodeCompiler compiler;
53                 CodeCompileUnit unit;
54                 CodeNamespace mainNS;
55                 CompilerParameters compilerParameters;
56 #if NET_2_0
57                 bool isRebuilding = false;
58                 protected Hashtable partialNameOverride = new Hashtable();
59 #endif
60                 protected CodeTypeDeclaration mainClass;
61                 protected CodeTypeReferenceExpression mainClassExpr;
62                 protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
63
64                 protected BaseCompiler (TemplateParser parser)
65                 {
66                         compilerParameters = new CompilerParameters ();
67                         this.parser = parser;
68                 }
69
70                 void Init ()
71                 {
72                         unit = new CodeCompileUnit ();
73 #if NET_2_0
74                         if (parser.IsPartial) {
75                                 string ns = null;
76                                 string classtype = parser.PartialClassName;
77
78                                 if (classtype.Contains (".")) {
79                                         int dot = classtype.LastIndexOf (".");
80                                         ns = classtype.Substring (0, dot);
81                                         classtype = classtype.Substring (dot + 1);
82                                 }
83                                 
84                                 mainNS = new CodeNamespace (ns);
85                                 mainClass = new CodeTypeDeclaration (classtype);
86                                 mainClass.IsPartial = true;     
87                                 mainClassExpr = new CodeTypeReferenceExpression (parser.PartialClassName);
88                         } else {
89 #endif
90                         mainNS = new CodeNamespace ("ASP");
91                         mainClass = new CodeTypeDeclaration (parser.ClassName);
92                         CodeTypeReference baseTypeRef = new CodeTypeReference (parser.BaseType.FullName);
93                         if (parser.BaseTypeIsGlobal)
94                                 baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
95                         mainClass.BaseTypes.Add (baseTypeRef);
96                         mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
97 #if NET_2_0
98                         }
99 #endif
100                         unit.Namespaces.Add (mainNS);
101                         mainClass.TypeAttributes = TypeAttributes.Public;
102                         mainNS.Types.Add (mainClass);
103
104                         foreach (object o in parser.Imports) {
105                                 if (o is string)
106                                         mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
107                         }
108
109                         if (parser.Assemblies != null) {
110                                 foreach (object o in parser.Assemblies) {
111                                         if (o is string)
112                                                 unit.ReferencedAssemblies.Add ((string) o);
113                                 }
114                         }
115
116 #if NET_2_0
117                         ArrayList al = WebConfigurationManager.ExtraAssemblies;
118                         if (al != null && al.Count > 0) {
119                                 foreach (object o in al)
120                                         if (o is string)
121                                                 unit.ReferencedAssemblies.Add ((string) o);
122                         }
123
124                         IList list = BuildManager.CodeAssemblies;
125                         if (list != null && list.Count > 0) {
126                                 foreach (object o in list)
127                                         if (o is string)
128                                                 unit.ReferencedAssemblies.Add ((string) o);
129                         }
130 #endif
131                         // Late-bound generators specifics (as for MonoBASIC/VB.NET)
132                         unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
133                         unit.UserData["AllowLateBound"] = !parser.StrictOn;
134                         
135                         AddInterfaces ();
136                         AddClassAttributes ();
137                         CreateStaticFields ();
138                         AddApplicationAndSessionObjects ();
139                         AddScripts ();
140                         CreateMethods ();
141                         CreateConstructor (null, null);
142                 }
143
144 #if NET_2_0
145                 internal CodeDomProvider Provider {
146                         get { return provider; }
147                 }
148
149                 internal CodeCompileUnit CompileUnit {
150                         get { return unit; }
151                 }
152 #endif
153                 protected virtual void CreateStaticFields ()
154                 {
155                         CodeMemberField fld = new CodeMemberField (typeof (bool), "__initialized");
156                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
157                         fld.InitExpression = new CodePrimitiveExpression (false);
158                         mainClass.Members.Add (fld);
159                 }
160
161                 protected virtual void CreateConstructor (CodeStatementCollection localVars,
162                                                           CodeStatementCollection trueStmt)
163                 {
164                         CodeConstructor ctor = new CodeConstructor ();
165                         ctor.Attributes = MemberAttributes.Public;
166                         mainClass.Members.Add (ctor);
167
168                         if (localVars != null)
169                                 ctor.Statements.AddRange (localVars);
170
171                         CodeTypeReferenceExpression r;
172 #if NET_2_0
173                         if (parser.IsPartial)
174                                 r = new CodeTypeReferenceExpression (mainClass.Name);
175                         else
176 #endif
177                         r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
178                         CodeFieldReferenceExpression initialized;
179                         initialized = new CodeFieldReferenceExpression (r, "__initialized");
180                         
181                         CodeBinaryOperatorExpression bin;
182                         bin = new CodeBinaryOperatorExpression (initialized,
183                                                                 CodeBinaryOperatorType.ValueEquality,
184                                                                 new CodePrimitiveExpression (false));
185
186                         CodeAssignStatement assign = new CodeAssignStatement (initialized,
187                                                                               new CodePrimitiveExpression (true));
188
189                         CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
190                         if (trueStmt != null)
191                                 cond.TrueStatements.AddRange (trueStmt);
192                         
193                         ctor.Statements.Add (cond);
194                 }
195                 
196                 void AddScripts ()
197                 {
198                         if (parser.Scripts == null || parser.Scripts.Count == 0)
199                                 return;
200
201                         foreach (object o in parser.Scripts) {
202                                 if (o is string)
203                                         mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
204                         }
205                 }
206                 
207                 protected internal virtual void CreateMethods ()
208                 {
209                 }
210
211                 protected virtual void AddInterfaces ()
212                 {
213                         if (parser.Interfaces == null)
214                                 return;
215
216                         foreach (object o in parser.Interfaces) {
217                                 if (o is string)
218                                         mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
219                         }
220                 }
221
222                 protected virtual void AddClassAttributes ()
223                 {
224                 }
225                 
226                 protected virtual void AddApplicationAndSessionObjects ()
227                 {
228                 }
229
230                 /* Utility methods for <object> stuff */
231                 protected void CreateApplicationOrSessionPropertyForObject (Type type,
232                                                                             string propName,
233                                                                             bool isApplication,
234                                                                             bool isPublic)
235                 {
236                         /* if isApplication this generates (the 'cachedapp' field is created earlier):
237                         private MyNS.MyClass app {
238                                 get {
239                                         if ((this.cachedapp == null)) {
240                                                 this.cachedapp = ((MyNS.MyClass)
241                                                         (this.Application.StaticObjects.GetObject("app")));
242                                         }
243                                         return this.cachedapp;
244                                 }
245                         }
246
247                         else, this is for Session:
248                         private MyNS.MyClass ses {
249                                 get {
250                                         return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
251                                 }
252                         }
253
254                         */
255
256                         CodeExpression result = null;
257
258                         CodeMemberProperty prop = new CodeMemberProperty ();
259                         prop.Type = new CodeTypeReference (type);
260                         prop.Name = propName;
261                         if (isPublic)
262                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
263                         else
264                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
265
266                         CodePropertyReferenceExpression p1;
267                         if (isApplication)
268                                 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
269                         else
270                                 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
271
272                         CodePropertyReferenceExpression p2;
273                         p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
274
275                         CodeMethodReferenceExpression getobject;
276                         getobject = new CodeMethodReferenceExpression (p2, "GetObject");
277
278                         CodeMethodInvokeExpression invoker;
279                         invoker = new CodeMethodInvokeExpression (getobject,
280                                                 new CodePrimitiveExpression (propName));
281
282                         CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
283
284                         if (isApplication) {
285                                 CodeFieldReferenceExpression field;
286                                 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
287
288                                 CodeConditionStatement stmt = new CodeConditionStatement();
289                                 stmt.Condition = new CodeBinaryOperatorExpression (field,
290                                                         CodeBinaryOperatorType.IdentityEquality,
291                                                         new CodePrimitiveExpression (null));
292
293                                 CodeAssignStatement assign = new CodeAssignStatement ();
294                                 assign.Left = field;
295                                 assign.Right = cast;
296                                 stmt.TrueStatements.Add (assign);
297                                 prop.GetStatements.Add (stmt);
298                                 result = field;
299                         } else {
300                                 result = cast;
301                         }
302                                                 
303                         prop.GetStatements.Add (new CodeMethodReturnStatement (result));
304                         mainClass.Members.Add (prop);
305                 }
306
307                 protected string CreateFieldForObject (Type type, string name)
308                 {
309                         string fieldName = "cached" + name;
310                         CodeMemberField f = new CodeMemberField (type, fieldName);
311                         f.Attributes = MemberAttributes.Private;
312                         mainClass.Members.Add (f);
313                         return fieldName;
314                 }
315
316                 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
317                 {
318                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
319                         CodeMemberProperty prop = new CodeMemberProperty ();
320                         prop.Type = new CodeTypeReference (type);
321                         prop.Name = propName;
322                         if (isPublic)
323                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
324                         else
325                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
326
327                         CodeConditionStatement stmt = new CodeConditionStatement();
328                         stmt.Condition = new CodeBinaryOperatorExpression (field,
329                                                 CodeBinaryOperatorType.IdentityEquality,
330                                                 new CodePrimitiveExpression (null));
331
332                         CodeObjectCreateExpression create = new CodeObjectCreateExpression (prop.Type); 
333                         stmt.TrueStatements.Add (new CodeAssignStatement (field, create));
334                         prop.GetStatements.Add (stmt);
335                         prop.GetStatements.Add (new CodeMethodReturnStatement (field));
336
337                         mainClass.Members.Add (prop);
338                 }
339                 /******/
340
341                 void CheckCompilerErrors (CompilerResults results)
342                 {
343                         if (results.NativeCompilerReturnValue == 0)
344                                 return;
345
346                         StringWriter writer = new StringWriter();
347                         provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
348                         throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
349                 }
350
351                 protected string DynamicDir ()
352                 {
353                         return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
354                 }
355
356                 [MonoTODO ("find out how to extract the warningLevel and compilerOptions in the <system.codedom> case")]
357                 public virtual Type GetCompiledType () 
358                 {
359                         Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
360                         if (type != null)
361                                 return type;
362
363                         Init ();
364                         string lang = parser.Language;
365 #if NET_2_0
366                         CompilationSection config = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
367                         Compiler comp = config.Compilers[lang];
368
369                         string compilerOptions = "";
370                         int warningLevel = 0;
371
372                         if (comp == null) {
373                                 CompilerInfo info = CodeDomProvider.GetCompilerInfo (lang);
374                                 if (info != null && info.IsCodeDomProviderTypeValid)
375                                         provider = info.CreateProvider ();
376
377                                 // XXX there's no way to get
378                                 // warningLevel or compilerOptions out
379                                 // of the provider.. they're in the
380                                 // configuration section, though.
381                         }
382                         else {
383                                 Type t = Type.GetType (comp.Type, true);
384                                 provider = Activator.CreateInstance (t) as CodeDomProvider;
385
386                                 compilerOptions = comp.CompilerOptions;
387                                 warningLevel = comp.WarningLevel;
388                         }
389
390 #else
391                         CompilationConfiguration config;
392
393                         config = CompilationConfiguration.GetInstance (parser.Context);
394                         provider = config.GetProvider (lang);
395
396                         string compilerOptions = config.GetCompilerOptions (lang);
397                         int warningLevel = config.GetWarningLevel (lang);
398 #endif
399                         if (provider == null)
400                                 throw new HttpException ("Configuration error. Language not supported: " +
401                                                           lang, 500);
402
403                         compiler = provider.CreateCompiler ();
404
405                         compilerParameters.IncludeDebugInformation = parser.Debug;
406                         compilerParameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
407
408                         compilerParameters.WarningLevel = warningLevel;
409                         bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
410
411                         string tempdir = config.TempDirectory;
412                         if (tempdir == null || tempdir == "")
413                                 tempdir = DynamicDir ();
414                                 
415                         TempFileCollection tempcoll = new TempFileCollection (tempdir, keepFiles);
416                         compilerParameters.TempFiles = tempcoll;
417                         string dllfilename = Path.GetFileName (tempcoll.AddExtension ("dll", true));
418                         compilerParameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
419
420                         CompilerResults results = CachingCompiler.Compile (this);
421                         CheckCompilerErrors (results);
422                         Assembly assembly = results.CompiledAssembly;
423                         if (assembly == null) {
424                                 if (!File.Exists (compilerParameters.OutputAssembly)) {
425                                         results.TempFiles.Delete ();
426                                         throw new CompilationException (parser.InputFile, results.Errors,
427                                                 "No assembly returned after compilation!?");
428                                 }
429
430                                 assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
431                         }
432
433                         results.TempFiles.Delete ();
434                         Type mainClassType = assembly.GetType (mainClassExpr.Type.BaseType, true);
435
436 #if NET_2_0
437                         if (parser.IsPartial) {
438                                 // With the partial classes, we need to make sure we
439                                 // don't have any methods that should have not been
440                                 // created (because they are accessible from the base
441                                 // types). We cannot do this normally because the
442                                 // codebehind file is actually a partial class and we
443                                 // have no way of identifying the partial class' base
444                                 // type until now.
445                                 if (!isRebuilding && CheckPartialBaseType (mainClassType)) {
446                                         isRebuilding = true;
447                                         parser.RootBuilder.ResetState ();
448                                         return GetCompiledType ();
449                                 }
450                         }
451 #endif
452
453                         return mainClassType;
454                 }
455
456 #if NET_2_0
457                 internal bool IsRebuildingPartial
458                 {
459                         get { return isRebuilding; }
460                 }
461
462                 internal bool CheckPartialBaseType (Type type)
463                 {
464                         // Get the base type. If we don't have any (bad thing), we
465                         // don't need to replace ourselves. Also check for the
466                         // core file, since that won't have any either.
467                         Type baseType = type.BaseType;
468                         if (baseType == null || baseType == typeof(System.Web.UI.Page))
469                                 return false;
470
471                         bool rebuild = false;
472
473                         if (CheckPartialBaseFields (type, baseType))
474                                 rebuild = true;
475
476                         if (CheckPartialBaseProperties (type, baseType))
477                                 rebuild = true;
478
479                         return rebuild;
480                 }
481
482                 internal bool CheckPartialBaseFields (Type type, Type baseType)
483                 {
484                         bool rebuild = false;
485
486                         foreach (FieldInfo baseInfo in baseType.GetFields (replaceableFlags)) {
487                                 if (baseInfo.IsPrivate)
488                                         continue;
489
490                                 FieldInfo typeInfo = type.GetField (baseInfo.Name, replaceableFlags);
491
492                                 if (typeInfo != null && typeInfo.DeclaringType == type) {
493                                         partialNameOverride [typeInfo.Name] = true;
494                                         rebuild = true;
495                                 }
496                         }
497
498                         return rebuild;
499                 }
500
501                 internal bool CheckPartialBaseProperties (Type type, Type baseType)
502                 {
503                         bool rebuild = false;
504
505                         foreach (PropertyInfo baseInfo in baseType.GetProperties ()) {
506                                 PropertyInfo typeInfo = type.GetProperty (baseInfo.Name);
507
508                                 if (typeInfo != null && typeInfo.DeclaringType == type) {
509                                         partialNameOverride [typeInfo.Name] = true;
510                                         rebuild = true;
511                                 }
512                         }
513
514                         return rebuild;
515                 }
516 #endif
517
518                 internal CompilerParameters CompilerParameters {
519                         get { return compilerParameters; }
520                 }
521
522                 internal CodeCompileUnit Unit {
523                         get { return unit; }
524                 }
525
526                 internal virtual ICodeCompiler Compiler {
527                         get { return compiler; }
528                 }
529
530                 internal TemplateParser Parser {
531                         get { return parser; }
532                 }
533         }
534 }
535