merge -r 58060:58217
[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                 TemplateParser parser;
46                 CodeDomProvider provider;
47                 ICodeCompiler compiler;
48                 CodeCompileUnit unit;
49                 CodeNamespace mainNS;
50                 CompilerParameters compilerParameters;
51                 protected CodeTypeDeclaration mainClass;
52                 protected CodeTypeReferenceExpression mainClassExpr;
53                 protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
54
55                 protected BaseCompiler (TemplateParser parser)
56                 {
57                         compilerParameters = new CompilerParameters ();
58                         this.parser = parser;
59                 }
60
61                 void Init ()
62                 {
63                         unit = new CodeCompileUnit ();
64                         mainNS = new CodeNamespace ("ASP");
65                         unit.Namespaces.Add (mainNS);
66                         mainClass = new CodeTypeDeclaration (parser.ClassName);
67                         mainClass.TypeAttributes = TypeAttributes.Public;
68                         mainNS.Types.Add (mainClass);
69                         mainClass.BaseTypes.Add (new CodeTypeReference (parser.BaseType.FullName));
70                         mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
71                         foreach (object o in parser.Imports) {
72                                 if (o is string)
73                                         mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
74                         }
75
76                         if (parser.Assemblies != null) {
77                                 foreach (object o in parser.Assemblies) {
78                                         if (o is string)
79                                                 unit.ReferencedAssemblies.Add ((string) o);
80                                 }
81                         }
82
83                         // Late-bound generators specifics (as for MonoBASIC/VB.NET)
84                         unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
85                         unit.UserData["AllowLateBound"] = !parser.StrictOn;
86                         
87                         AddInterfaces ();
88                         AddClassAttributes ();
89                         CreateStaticFields ();
90                         AddApplicationAndSessionObjects ();
91                         AddScripts ();
92                         CreateConstructor (null, null);
93                 }
94
95                 protected virtual void CreateStaticFields ()
96                 {
97                         CodeMemberField fld = new CodeMemberField (typeof (bool), "__intialized");
98                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
99                         fld.InitExpression = new CodePrimitiveExpression (false);
100                         mainClass.Members.Add (fld);
101                 }
102
103                 protected virtual void CreateConstructor (CodeStatementCollection localVars,
104                                                           CodeStatementCollection trueStmt)
105                 {
106                         CodeConstructor ctor = new CodeConstructor ();
107                         ctor.Attributes = MemberAttributes.Public;
108                         mainClass.Members.Add (ctor);
109
110                         if (localVars != null)
111                                 ctor.Statements.AddRange (localVars);
112
113                         CodeTypeReferenceExpression r;
114                         r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
115                         CodeFieldReferenceExpression intialized;
116                         intialized = new CodeFieldReferenceExpression (r, "__intialized");
117                         
118                         CodeBinaryOperatorExpression bin;
119                         bin = new CodeBinaryOperatorExpression (intialized,
120                                                                 CodeBinaryOperatorType.ValueEquality,
121                                                                 new CodePrimitiveExpression (false));
122
123                         CodeAssignStatement assign = new CodeAssignStatement (intialized,
124                                                                               new CodePrimitiveExpression (true));
125
126                         CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
127                         if (trueStmt != null)
128                                 cond.TrueStatements.AddRange (trueStmt);
129                         
130                         ctor.Statements.Add (cond);
131                 }
132                 
133                 void AddScripts ()
134                 {
135                         if (parser.Scripts == null || parser.Scripts.Count == 0)
136                                 return;
137
138                         foreach (object o in parser.Scripts) {
139                                 if (o is string)
140                                         mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
141                         }
142                 }
143                 
144                 protected virtual void CreateMethods ()
145                 {
146                 }
147
148                 protected virtual void AddInterfaces ()
149                 {
150                         if (parser.Interfaces == null)
151                                 return;
152
153                         foreach (object o in parser.Interfaces) {
154                                 if (o is string)
155                                         mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
156                         }
157                 }
158
159                 protected virtual void AddClassAttributes ()
160                 {
161                 }
162                 
163                 protected virtual void AddApplicationAndSessionObjects ()
164                 {
165                 }
166
167                 /* Utility methods for <object> stuff */
168                 protected void CreateApplicationOrSessionPropertyForObject (Type type,
169                                                                             string propName,
170                                                                             bool isApplication,
171                                                                             bool isPublic)
172                 {
173                         /* if isApplication this generates (the 'cachedapp' field is created earlier):
174                         private MyNS.MyClass app {
175                                 get {
176                                         if ((this.cachedapp == null)) {
177                                                 this.cachedapp = ((MyNS.MyClass)
178                                                         (this.Application.StaticObjects.GetObject("app")));
179                                         }
180                                         return this.cachedapp;
181                                 }
182                         }
183
184                         else, this is for Session:
185                         private MyNS.MyClass ses {
186                                 get {
187                                         return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
188                                 }
189                         }
190
191                         */
192
193                         CodeExpression result = null;
194
195                         CodeMemberProperty prop = new CodeMemberProperty ();
196                         prop.Type = new CodeTypeReference (type);
197                         prop.Name = propName;
198                         if (isPublic)
199                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
200                         else
201                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
202
203                         CodePropertyReferenceExpression p1;
204                         if (isApplication)
205                                 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
206                         else
207                                 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
208
209                         CodePropertyReferenceExpression p2;
210                         p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
211
212                         CodeMethodReferenceExpression getobject;
213                         getobject = new CodeMethodReferenceExpression (p2, "GetObject");
214
215                         CodeMethodInvokeExpression invoker;
216                         invoker = new CodeMethodInvokeExpression (getobject,
217                                                 new CodePrimitiveExpression (propName));
218
219                         CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
220
221                         if (isApplication) {
222                                 CodeFieldReferenceExpression field;
223                                 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
224
225                                 CodeConditionStatement stmt = new CodeConditionStatement();
226                                 stmt.Condition = new CodeBinaryOperatorExpression (field,
227                                                         CodeBinaryOperatorType.IdentityEquality,
228                                                         new CodePrimitiveExpression (null));
229
230                                 CodeAssignStatement assign = new CodeAssignStatement ();
231                                 assign.Left = field;
232                                 assign.Right = cast;
233                                 stmt.TrueStatements.Add (assign);
234                                 prop.GetStatements.Add (stmt);
235                                 result = field;
236                         } else {
237                                 result = cast;
238                         }
239                                                 
240                         prop.GetStatements.Add (new CodeMethodReturnStatement (result));
241                         mainClass.Members.Add (prop);
242                 }
243
244                 protected string CreateFieldForObject (Type type, string name)
245                 {
246                         string fieldName = "cached" + name;
247                         CodeMemberField f = new CodeMemberField (type, fieldName);
248                         f.Attributes = MemberAttributes.Private;
249                         mainClass.Members.Add (f);
250                         return fieldName;
251                 }
252
253                 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
254                 {
255                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
256                         CodeMemberProperty prop = new CodeMemberProperty ();
257                         prop.Type = new CodeTypeReference (type);
258                         prop.Name = propName;
259                         if (isPublic)
260                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
261                         else
262                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
263
264                         CodeConditionStatement stmt = new CodeConditionStatement();
265                         stmt.Condition = new CodeBinaryOperatorExpression (field,
266                                                 CodeBinaryOperatorType.IdentityEquality,
267                                                 new CodePrimitiveExpression (null));
268
269                         CodeObjectCreateExpression create = new CodeObjectCreateExpression (prop.Type); 
270                         stmt.TrueStatements.Add (new CodeAssignStatement (field, create));
271                         prop.GetStatements.Add (stmt);
272                         prop.GetStatements.Add (new CodeMethodReturnStatement (field));
273
274                         mainClass.Members.Add (prop);
275                 }
276                 /******/
277
278                 void CheckCompilerErrors (CompilerResults results)
279                 {
280                         if (results.NativeCompilerReturnValue == 0)
281                                 return;
282
283                         StringWriter writer = new StringWriter();
284                         provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
285                         throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
286                 }
287
288                 protected string DynamicDir ()
289                 {
290                         return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
291                 }
292                 
293                 public virtual Type GetCompiledType () 
294                 {
295                         Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
296                         if (type != null)
297                                 return type;
298
299                         Init ();
300                         string lang = parser.Language;
301                         CompilationConfiguration config;
302
303                         config = CompilationConfiguration.GetInstance (parser.Context);
304                         provider = config.GetProvider (lang);
305                         if (provider == null)
306                                 throw new HttpException ("Configuration error. Language not supported: " +
307                                                           lang, 500);
308
309                         compiler = provider.CreateCompiler ();
310
311                         CreateMethods ();
312                         compilerParameters.IncludeDebugInformation = parser.Debug;
313                         compilerParameters.CompilerOptions = config.GetCompilerOptions (lang) + " " +
314                                                              parser.CompilerOptions;
315
316                         compilerParameters.WarningLevel = config.GetWarningLevel (lang);
317                         bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
318
319                         string tempdir = config.TempDirectory;
320                         if (tempdir == null || tempdir == "")
321                                 tempdir = DynamicDir ();
322                                 
323                         TempFileCollection tempcoll = new TempFileCollection (tempdir, keepFiles);
324                         compilerParameters.TempFiles = tempcoll;
325                         string dllfilename = Path.GetFileName (tempcoll.AddExtension ("dll", true));
326                         compilerParameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
327
328                         CompilerResults results = CachingCompiler.Compile (this);
329                         CheckCompilerErrors (results);
330                         Assembly assembly = results.CompiledAssembly;
331                         if (assembly == null) {
332                                 if (!File.Exists (compilerParameters.OutputAssembly))
333                                         throw new CompilationException (parser.InputFile, results.Errors,
334                                                 "No assembly returned after compilation!?");
335
336                                 assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
337                         }
338
339                         results.TempFiles.Delete ();
340                         return assembly.GetType (mainClassExpr.Type.BaseType, true);
341                 }
342
343                 internal CompilerParameters CompilerParameters {
344                         get { return compilerParameters; }
345                 }
346
347                 internal CodeCompileUnit Unit {
348                         get { return unit; }
349                 }
350
351                 internal virtual ICodeCompiler Compiler {
352                         get { return compiler; }
353                 }
354
355                 internal TemplateParser Parser {
356                         get { return parser; }
357                 }
358         }
359 }
360