2004-03-15 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[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) 2002,2003 Ximian, Inc (http://www.ximian.com)
8 //
9
10 using System;
11 using System.CodeDom;
12 using System.CodeDom.Compiler;
13 using System.Collections;
14 using System.Reflection;
15 using System.Text;
16 using System.Web.UI;
17 using System.Web.Configuration;
18 using System.IO;
19
20 namespace System.Web.Compilation
21 {
22         abstract class BaseCompiler
23         {
24                 TemplateParser parser;
25                 CodeDomProvider provider;
26                 ICodeCompiler compiler;
27                 CodeCompileUnit unit;
28                 CodeNamespace mainNS;
29                 CompilerParameters compilerParameters;
30                 protected CodeTypeDeclaration mainClass;
31                 protected CodeTypeReferenceExpression mainClassExpr;
32                 protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
33
34                 protected BaseCompiler (TemplateParser parser)
35                 {
36                         compilerParameters = new CompilerParameters ();
37                         this.parser = parser;
38                 }
39
40                 void Init ()
41                 {
42                         unit = new CodeCompileUnit ();
43                         mainNS = new CodeNamespace ("ASP");
44                         unit.Namespaces.Add (mainNS);
45                         mainClass = new CodeTypeDeclaration (parser.ClassName);
46                         mainClass.TypeAttributes = TypeAttributes.Public;
47                         mainNS.Types.Add (mainClass);
48                         mainClass.BaseTypes.Add (new CodeTypeReference (parser.BaseType.FullName));
49                         mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
50                         foreach (object o in parser.Imports) {
51                                 if (o is string)
52                                         mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
53                         }
54
55                         if (parser.Assemblies != null) {
56                                 foreach (object o in parser.Assemblies) {
57                                         if (o is string)
58                                                 unit.ReferencedAssemblies.Add ((string) o);
59                                 }
60                         }
61
62                         AddInterfaces ();
63                         AddClassAttributes ();
64                         CreateStaticFields ();
65                         AddApplicationAndSessionObjects ();
66                         AddScripts ();
67                         CreateConstructor (null, null);
68                 }
69
70                 protected virtual void CreateStaticFields ()
71                 {
72                         CodeMemberField fld = new CodeMemberField (typeof (bool), "__intialized");
73                         fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
74                         fld.InitExpression = new CodePrimitiveExpression (false);
75                         mainClass.Members.Add (fld);
76                 }
77
78                 protected virtual void CreateConstructor (CodeStatementCollection localVars,
79                                                           CodeStatementCollection trueStmt)
80                 {
81                         CodeConstructor ctor = new CodeConstructor ();
82                         ctor.Attributes = MemberAttributes.Public;
83                         mainClass.Members.Add (ctor);
84
85                         if (localVars != null)
86                                 ctor.Statements.AddRange (localVars);
87
88                         CodeTypeReferenceExpression r;
89                         r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
90                         CodeFieldReferenceExpression intialized;
91                         intialized = new CodeFieldReferenceExpression (r, "__intialized");
92                         
93                         CodeBinaryOperatorExpression bin;
94                         bin = new CodeBinaryOperatorExpression (intialized,
95                                                                 CodeBinaryOperatorType.ValueEquality,
96                                                                 new CodePrimitiveExpression (false));
97
98                         CodeAssignStatement assign = new CodeAssignStatement (intialized,
99                                                                               new CodePrimitiveExpression (true));
100
101                         CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
102                         if (trueStmt != null)
103                                 cond.TrueStatements.AddRange (trueStmt);
104                         
105                         ctor.Statements.Add (cond);
106                 }
107                 
108                 void AddScripts ()
109                 {
110                         if (parser.Scripts == null || parser.Scripts.Count == 0)
111                                 return;
112
113                         foreach (object o in parser.Scripts) {
114                                 if (o is string)
115                                         mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
116                         }
117                 }
118                 
119                 protected virtual void CreateMethods ()
120                 {
121                 }
122
123                 protected virtual void AddInterfaces ()
124                 {
125                         if (parser.Interfaces == null)
126                                 return;
127
128                         foreach (object o in parser.Interfaces) {
129                                 if (o is string)
130                                         mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
131                         }
132                 }
133
134                 protected virtual void AddClassAttributes ()
135                 {
136                 }
137                 
138                 protected virtual void AddApplicationAndSessionObjects ()
139                 {
140                 }
141
142                 /* Utility methods for <object> stuff */
143                 protected void CreateApplicationOrSessionPropertyForObject (Type type,
144                                                                             string propName,
145                                                                             bool isApplication,
146                                                                             bool isPublic)
147                 {
148                         /* if isApplication this generates (the 'cachedapp' field is created earlier):
149                         private MyNS.MyClass app {
150                                 get {
151                                         if ((this.cachedapp == null)) {
152                                                 this.cachedapp = ((MyNS.MyClass)
153                                                         (this.Application.StaticObjects.GetObject("app")));
154                                         }
155                                         return this.cachedapp;
156                                 }
157                         }
158
159                         else, this is for Session:
160                         private MyNS.MyClass ses {
161                                 get {
162                                         return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
163                                 }
164                         }
165
166                         */
167
168                         CodeExpression result = null;
169
170                         CodeMemberProperty prop = new CodeMemberProperty ();
171                         prop.Type = new CodeTypeReference (type);
172                         prop.Name = propName;
173                         if (isPublic)
174                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
175                         else
176                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
177
178                         CodePropertyReferenceExpression p1;
179                         if (isApplication)
180                                 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
181                         else
182                                 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
183
184                         CodePropertyReferenceExpression p2;
185                         p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
186
187                         CodeMethodReferenceExpression getobject;
188                         getobject = new CodeMethodReferenceExpression (p2, "GetObject");
189
190                         CodeMethodInvokeExpression invoker;
191                         invoker = new CodeMethodInvokeExpression (getobject,
192                                                 new CodePrimitiveExpression (propName));
193
194                         CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
195
196                         if (isApplication) {
197                                 CodeFieldReferenceExpression field;
198                                 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
199
200                                 CodeConditionStatement stmt = new CodeConditionStatement();
201                                 stmt.Condition = new CodeBinaryOperatorExpression (field,
202                                                         CodeBinaryOperatorType.IdentityEquality,
203                                                         new CodePrimitiveExpression (null));
204
205                                 CodeAssignStatement assign = new CodeAssignStatement ();
206                                 assign.Left = field;
207                                 assign.Right = cast;
208                                 stmt.TrueStatements.Add (assign);
209                                 prop.GetStatements.Add (stmt);
210                                 result = field;
211                         } else {
212                                 result = cast;
213                         }
214                                                 
215                         prop.GetStatements.Add (new CodeMethodReturnStatement (result));
216                         mainClass.Members.Add (prop);
217                 }
218
219                 protected string CreateFieldForObject (Type type, string name)
220                 {
221                         string fieldName = "cached" + name;
222                         CodeMemberField f = new CodeMemberField (type, fieldName);
223                         f.Attributes = MemberAttributes.Private;
224                         mainClass.Members.Add (f);
225                         return fieldName;
226                 }
227
228                 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
229                 {
230                         CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
231                         CodeMemberProperty prop = new CodeMemberProperty ();
232                         prop.Type = new CodeTypeReference (type);
233                         prop.Name = propName;
234                         if (isPublic)
235                                 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
236                         else
237                                 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
238
239                         CodeConditionStatement stmt = new CodeConditionStatement();
240                         stmt.Condition = new CodeBinaryOperatorExpression (field,
241                                                 CodeBinaryOperatorType.IdentityEquality,
242                                                 new CodePrimitiveExpression (null));
243
244                         CodeObjectCreateExpression create = new CodeObjectCreateExpression (prop.Type); 
245                         stmt.TrueStatements.Add (new CodeAssignStatement (field, create));
246                         prop.GetStatements.Add (stmt);
247                         prop.GetStatements.Add (new CodeMethodReturnStatement (field));
248
249                         mainClass.Members.Add (prop);
250                 }
251                 /******/
252
253                 void CheckCompilerErrors (CompilerResults results)
254                 {
255                         if (results.NativeCompilerReturnValue == 0)
256                                 return;
257
258                         StringWriter writer = new StringWriter();
259                         provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
260                         throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
261                 }
262
263                 public virtual Type GetCompiledType () 
264                 {
265                         Init ();
266                         CompilationCacheItem item = CachingCompiler.GetCached (parser.InputFile);
267                         if (item != null) {
268                                 Assembly a = item.Result.CompiledAssembly;
269                                 if (a != null)
270                                         return a.GetType (mainClassExpr.Type.BaseType, true);
271                         }
272
273                         string lang = parser.Language;
274                         CompilationConfiguration config;
275
276                         config = CompilationConfiguration.GetInstance (parser.Context);
277                         provider = config.GetProvider (lang);
278                         if (provider == null)
279                                 throw new HttpException ("Configuration error. Language not supported: " +
280                                                           lang, 500);
281
282                         compiler = provider.CreateCompiler ();
283
284                         CreateMethods ();
285                         compilerParameters.IncludeDebugInformation = parser.Debug;
286                         compilerParameters.CompilerOptions = config.GetCompilerOptions (lang) + " " +
287                                                              parser.CompilerOptions;
288
289                         compilerParameters.WarningLevel = config.GetWarningLevel (lang);
290                         compilerParameters.TempFiles = new TempFileCollection (config.TempDirectory);
291
292                         CompilerResults results = CachingCompiler.Compile (this);
293                         CheckCompilerErrors (results);
294                         if (results.CompiledAssembly == null)
295                                 throw new CompilationException (parser.InputFile, results.Errors,
296                                         "No assembly returned after compilation!?");
297
298                         return results.CompiledAssembly.GetType (mainClassExpr.Type.BaseType, true);
299                 }
300
301                 internal CompilerParameters CompilerParameters {
302                         get { return compilerParameters; }
303                 }
304
305                 internal CodeCompileUnit Unit {
306                         get { return unit; }
307                 }
308
309                 internal virtual ICodeCompiler Compiler {
310                         get { return compiler; }
311                 }
312
313                 internal TemplateParser Parser {
314                         get { return parser; }
315                 }
316         }
317 }
318