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