* BaseCompiler.cs: Fixed build on 1.0 profile.
[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 NET_2_0
94                         if (parser.BaseTypeIsGlobal)
95                                 baseTypeRef.Options |= CodeTypeReferenceOptions.GlobalReference;
96 #endif
97                         mainClass.BaseTypes.Add (baseTypeRef);
98                         mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
99 #if NET_2_0
100                         }
101 #endif
102                         unit.Namespaces.Add (mainNS);
103                         mainClass.TypeAttributes = TypeAttributes.Public;
104                         mainNS.Types.Add (mainClass);
105
106                         foreach (object o in parser.Imports) {
107                                 if (o is string)
108                                         mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
109                         }
110
111                         if (parser.Assemblies != null) {
112                                 foreach (object o in parser.Assemblies) {
113                                         if (o is string)
114                                                 unit.ReferencedAssemblies.Add ((string) o);
115                                 }
116                         }
117
118 #if NET_2_0
119                         ArrayList al = WebConfigurationManager.ExtraAssemblies;
120                         if (al != null && al.Count > 0) {
121                                 foreach (object o in al)
122                                         if (o is string)
123                                                 unit.ReferencedAssemblies.Add ((string) o);
124                         }
125
126                         IList list = BuildManager.CodeAssemblies;
127                         if (list != null && list.Count > 0) {
128                                 foreach (object o in list)
129                                         if (o is string)
130                                                 unit.ReferencedAssemblies.Add ((string) o);
131                         }
132 #endif
133                         // Late-bound generators specifics (as for MonoBASIC/VB.NET)
134                         unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
135                         unit.UserData["AllowLateBound"] = !parser.StrictOn;
136                         
137                         AddInterfaces ();
138                         AddClassAttributes ();
139                         CreateStaticFields ();
140                         AddApplicationAndSessionObjects ();
141                         AddScripts ();
142                         CreateMethods ();
143                         CreateConstructor (null, null);
144                 }
145
146 #if NET_2_0
147                 internal CodeDomProvider Provider {
148                         get { return provider; }
149                 }
150
151                 internal CodeCompileUnit CompileUnit {
152                         get { return unit; }
153                 }
154 #endif
155                 protected virtual void CreateStaticFields ()
156                 {
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);
161                 }
162
163                 protected virtual void CreateConstructor (CodeStatementCollection localVars,
164                                                           CodeStatementCollection trueStmt)
165                 {
166                         CodeConstructor ctor = new CodeConstructor ();
167                         ctor.Attributes = MemberAttributes.Public;
168                         mainClass.Members.Add (ctor);
169
170                         if (localVars != null)
171                                 ctor.Statements.AddRange (localVars);
172
173                         CodeTypeReferenceExpression r;
174 #if NET_2_0
175                         if (parser.IsPartial)
176                                 r = new CodeTypeReferenceExpression (mainClass.Name);
177                         else
178 #endif
179                         r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
180                         CodeFieldReferenceExpression initialized;
181                         initialized = new CodeFieldReferenceExpression (r, "__initialized");
182                         
183                         CodeBinaryOperatorExpression bin;
184                         bin = new CodeBinaryOperatorExpression (initialized,
185                                                                 CodeBinaryOperatorType.ValueEquality,
186                                                                 new CodePrimitiveExpression (false));
187
188                         CodeAssignStatement assign = new CodeAssignStatement (initialized,
189                                                                               new CodePrimitiveExpression (true));
190
191                         CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
192                         if (trueStmt != null)
193                                 cond.TrueStatements.AddRange (trueStmt);
194                         
195                         ctor.Statements.Add (cond);
196                 }
197                 
198                 void AddScripts ()
199                 {
200                         if (parser.Scripts == null || parser.Scripts.Count == 0)
201                                 return;
202
203                         foreach (object o in parser.Scripts) {
204                                 if (o is string)
205                                         mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
206                         }
207                 }
208                 
209                 protected internal virtual void CreateMethods ()
210                 {
211                 }
212
213                 protected virtual void AddInterfaces ()
214                 {
215                         if (parser.Interfaces == null)
216                                 return;
217
218                         foreach (object o in parser.Interfaces) {
219                                 if (o is string)
220                                         mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
221                         }
222                 }
223
224                 protected virtual void AddClassAttributes ()
225                 {
226                 }
227                 
228                 protected virtual void AddApplicationAndSessionObjects ()
229                 {
230                 }
231
232                 /* Utility methods for <object> stuff */
233                 protected void CreateApplicationOrSessionPropertyForObject (Type type,
234                                                                             string propName,
235                                                                             bool isApplication,
236                                                                             bool isPublic)
237                 {
238                         /* if isApplication this generates (the 'cachedapp' field is created earlier):
239                         private MyNS.MyClass app {
240                                 get {
241                                         if ((this.cachedapp == null)) {
242                                                 this.cachedapp = ((MyNS.MyClass)
243                                                         (this.Application.StaticObjects.GetObject("app")));
244                                         }
245                                         return this.cachedapp;
246                                 }
247                         }
248
249                         else, this is for Session:
250                         private MyNS.MyClass ses {
251                                 get {
252                                         return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
253                                 }
254                         }
255
256                         */
257
258                         CodeExpression result = null;
259
260                         CodeMemberProperty prop = new CodeMemberProperty ();
261                         prop.Type = new CodeTypeReference (type);
262                         prop.Name = propName;
263                         if (isPublic)
264                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
265                         else
266                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
267
268                         CodePropertyReferenceExpression p1;
269                         if (isApplication)
270                                 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
271                         else
272                                 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
273
274                         CodePropertyReferenceExpression p2;
275                         p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
276
277                         CodeMethodReferenceExpression getobject;
278                         getobject = new CodeMethodReferenceExpression (p2, "GetObject");
279
280                         CodeMethodInvokeExpression invoker;
281                         invoker = new CodeMethodInvokeExpression (getobject,
282                                                 new CodePrimitiveExpression (propName));
283
284                         CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
285
286                         if (isApplication) {
287                                 CodeFieldReferenceExpression field;
288                                 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
289
290                                 CodeConditionStatement stmt = new CodeConditionStatement();
291                                 stmt.Condition = new CodeBinaryOperatorExpression (field,
292                                                         CodeBinaryOperatorType.IdentityEquality,
293                                                         new CodePrimitiveExpression (null));
294
295                                 CodeAssignStatement assign = new CodeAssignStatement ();
296                                 assign.Left = field;
297                                 assign.Right = cast;
298                                 stmt.TrueStatements.Add (assign);
299                                 prop.GetStatements.Add (stmt);
300                                 result = field;
301                         } else {
302                                 result = cast;
303                         }
304                                                 
305                         prop.GetStatements.Add (new CodeMethodReturnStatement (result));
306                         mainClass.Members.Add (prop);
307                 }
308
309                 protected string CreateFieldForObject (Type type, string name)
310                 {
311                         string fieldName = "cached" + name;
312                         CodeMemberField f = new CodeMemberField (type, fieldName);
313                         f.Attributes = MemberAttributes.Private;
314                         mainClass.Members.Add (f);
315                         return fieldName;
316                 }
317
318                 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
319                 {
320                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
321                         CodeMemberProperty prop = new CodeMemberProperty ();
322                         prop.Type = new CodeTypeReference (type);
323                         prop.Name = propName;
324                         if (isPublic)
325                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
326                         else
327                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
328
329                         CodeConditionStatement stmt = new CodeConditionStatement();
330                         stmt.Condition = new CodeBinaryOperatorExpression (field,
331                                                 CodeBinaryOperatorType.IdentityEquality,
332                                                 new CodePrimitiveExpression (null));
333
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));
338
339                         mainClass.Members.Add (prop);
340                 }
341                 /******/
342
343                 void CheckCompilerErrors (CompilerResults results)
344                 {
345                         if (results.NativeCompilerReturnValue == 0)
346                                 return;
347
348                         StringWriter writer = new StringWriter();
349                         provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
350                         throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
351                 }
352
353                 protected string DynamicDir ()
354                 {
355                         return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
356                 }
357
358                 [MonoTODO ("find out how to extract the warningLevel and compilerOptions in the <system.codedom> case")]
359                 public virtual Type GetCompiledType () 
360                 {
361                         Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
362                         if (type != null)
363                                 return type;
364
365                         Init ();
366                         string lang = parser.Language;
367 #if NET_2_0
368                         CompilationSection config = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
369                         Compiler comp = config.Compilers[lang];
370
371                         string compilerOptions = "";
372                         int warningLevel = 0;
373
374                         if (comp == null) {
375                                 CompilerInfo info = CodeDomProvider.GetCompilerInfo (lang);
376                                 if (info != null && info.IsCodeDomProviderTypeValid)
377                                         provider = info.CreateProvider ();
378
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.
383                         }
384                         else {
385                                 Type t = Type.GetType (comp.Type, true);
386                                 provider = Activator.CreateInstance (t) as CodeDomProvider;
387
388                                 compilerOptions = comp.CompilerOptions;
389                                 warningLevel = comp.WarningLevel;
390                         }
391
392 #else
393                         CompilationConfiguration config;
394
395                         config = CompilationConfiguration.GetInstance (parser.Context);
396                         provider = config.GetProvider (lang);
397
398                         string compilerOptions = config.GetCompilerOptions (lang);
399                         int warningLevel = config.GetWarningLevel (lang);
400 #endif
401                         if (provider == null)
402                                 throw new HttpException ("Configuration error. Language not supported: " +
403                                                           lang, 500);
404
405                         compiler = provider.CreateCompiler ();
406
407                         compilerParameters.IncludeDebugInformation = parser.Debug;
408                         compilerParameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
409
410                         compilerParameters.WarningLevel = warningLevel;
411                         bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
412
413                         string tempdir = config.TempDirectory;
414                         if (tempdir == null || tempdir == "")
415                                 tempdir = DynamicDir ();
416                                 
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);
421
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!?");
430                                 }
431
432                                 assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
433                         }
434
435                         results.TempFiles.Delete ();
436                         Type mainClassType = assembly.GetType (mainClassExpr.Type.BaseType, true);
437
438 #if NET_2_0
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
446                                 // type until now.
447                                 if (!isRebuilding && CheckPartialBaseType (mainClassType)) {
448                                         isRebuilding = true;
449                                         parser.RootBuilder.ResetState ();
450                                         return GetCompiledType ();
451                                 }
452                         }
453 #endif
454
455                         return mainClassType;
456                 }
457
458 #if NET_2_0
459                 internal bool IsRebuildingPartial
460                 {
461                         get { return isRebuilding; }
462                 }
463
464                 internal bool CheckPartialBaseType (Type type)
465                 {
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))
471                                 return false;
472
473                         bool rebuild = false;
474
475                         if (CheckPartialBaseFields (type, baseType))
476                                 rebuild = true;
477
478                         if (CheckPartialBaseProperties (type, baseType))
479                                 rebuild = true;
480
481                         return rebuild;
482                 }
483
484                 internal bool CheckPartialBaseFields (Type type, Type baseType)
485                 {
486                         bool rebuild = false;
487
488                         foreach (FieldInfo baseInfo in baseType.GetFields (replaceableFlags)) {
489                                 if (baseInfo.IsPrivate)
490                                         continue;
491
492                                 FieldInfo typeInfo = type.GetField (baseInfo.Name, replaceableFlags);
493
494                                 if (typeInfo != null && typeInfo.DeclaringType == type) {
495                                         partialNameOverride [typeInfo.Name] = true;
496                                         rebuild = true;
497                                 }
498                         }
499
500                         return rebuild;
501                 }
502
503                 internal bool CheckPartialBaseProperties (Type type, Type baseType)
504                 {
505                         bool rebuild = false;
506
507                         foreach (PropertyInfo baseInfo in baseType.GetProperties ()) {
508                                 PropertyInfo typeInfo = type.GetProperty (baseInfo.Name);
509
510                                 if (typeInfo != null && typeInfo.DeclaringType == type) {
511                                         partialNameOverride [typeInfo.Name] = true;
512                                         rebuild = true;
513                                 }
514                         }
515
516                         return rebuild;
517                 }
518 #endif
519
520                 internal CompilerParameters CompilerParameters {
521                         get { return compilerParameters; }
522                 }
523
524                 internal CodeCompileUnit Unit {
525                         get { return unit; }
526                 }
527
528                 internal virtual ICodeCompiler Compiler {
529                         get { return compiler; }
530                 }
531
532                 internal TemplateParser Parser {
533                         get { return parser; }
534                 }
535         }
536 }
537