2 // System.Web.Compilation.PageCompiler
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System.Collections;
33 using System.Collections.Generic;
35 using System.Reflection;
37 using System.Web.Configuration;
39 using System.Web.SessionState;
40 using System.Web.Util;
41 using System.Web.Profile;
43 namespace System.Web.Compilation
45 class PageCompiler : TemplateControlCompiler
47 PageParser pageParser;
48 static CodeTypeReference intRef = new CodeTypeReference (typeof (int));
50 public PageCompiler (PageParser pageParser)
53 this.pageParser = pageParser;
56 protected override void CreateStaticFields ()
58 base.CreateStaticFields ();
60 CodeMemberField fld = new CodeMemberField (typeof (object), "__fileDependencies");
61 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
62 fld.InitExpression = new CodePrimitiveExpression (null);
63 mainClass.Members.Add (fld);
65 if (pageParser.OutputCache) {
66 fld = new CodeMemberField (typeof (OutputCacheParameters), "__outputCacheSettings");
67 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
68 fld.InitExpression = new CodePrimitiveExpression (null);
69 mainClass.Members.Add (fld);
73 protected override void CreateConstructor (CodeStatementCollection localVars,
74 CodeStatementCollection trueStmt)
76 MainDirectiveAttribute <string> masterPageFile = pageParser.MasterPageFile;
77 if (masterPageFile != null && !masterPageFile.IsExpression)
78 // This is here just to trigger master page build, so that its type
79 // is available when compiling the page itself.
80 BuildManager.GetCompiledType (masterPageFile.Value);
82 MainDirectiveAttribute <string> clientTarget;
83 clientTarget = pageParser.ClientTarget;
84 if (clientTarget != null) {
86 prop = new CodePropertyReferenceExpression (thisRef, "ClientTarget");
87 CodeExpression ct = null;
89 if (clientTarget.IsExpression) {
90 var pi = GetFieldOrProperty (typeof (Page), "ClientTarget") as PropertyInfo;
92 ct = CompileExpression (pi, pi.PropertyType, clientTarget.UnparsedValue, false);
96 ct = new CodePrimitiveExpression (clientTarget.Value);
97 if (localVars == null)
98 localVars = new CodeStatementCollection ();
99 localVars.Add (new CodeAssignStatement (prop, ct));
102 ArrayList deps = pageParser.Dependencies;
103 int depsCount = deps != null ? deps.Count : 0;
106 if (localVars == null)
107 localVars = new CodeStatementCollection ();
108 if (trueStmt == null)
109 trueStmt = new CodeStatementCollection ();
111 CodeAssignStatement assign;
113 new CodeVariableDeclarationStatement (
118 CodeVariableReferenceExpression dependencies = new CodeVariableReferenceExpression ("dependencies");
120 new CodeAssignStatement (dependencies, new CodeArrayCreateExpression (typeof (string), depsCount))
123 CodeArrayIndexerExpression arrayIndex;
126 for (int i = 0; i < depsCount; i++) {
128 arrayIndex = new CodeArrayIndexerExpression (dependencies, new CodeExpression[] {new CodePrimitiveExpression (i)});
129 assign = new CodeAssignStatement (arrayIndex, new CodePrimitiveExpression (o));
130 trueStmt.Add (assign);
133 CodeMethodInvokeExpression getDepsCall = new CodeMethodInvokeExpression (
135 "GetWrappedFileDependencies",
136 new CodeExpression[] {dependencies}
138 assign = new CodeAssignStatement (GetMainClassFieldReferenceExpression ("__fileDependencies"), getDepsCall);
140 trueStmt.Add (assign);
143 base.CreateConstructor (localVars, trueStmt);
146 protected override void AddInterfaces ()
148 base.AddInterfaces ();
149 CodeTypeReference cref;
151 if (pageParser.EnableSessionState) {
152 cref = new CodeTypeReference (typeof (IRequiresSessionState));
153 if (partialClass != null)
154 partialClass.BaseTypes.Add (cref);
156 mainClass.BaseTypes.Add (cref);
159 if (pageParser.ReadOnlySessionState) {
160 cref = new CodeTypeReference (typeof (IReadOnlySessionState));
161 if (partialClass != null)
162 partialClass.BaseTypes.Add (cref);
164 mainClass.BaseTypes.Add (cref);
167 if (pageParser.Async)
168 mainClass.BaseTypes.Add (new CodeTypeReference (typeof (System.Web.IHttpAsyncHandler)));
170 mainClass.BaseTypes.Add (new CodeTypeReference (typeof (System.Web.IHttpHandler)));
173 void CreateGetTypeHashCode ()
175 CodeMemberMethod method = new CodeMemberMethod ();
176 method.ReturnType = intRef;
177 method.Name = "GetTypeHashCode";
178 method.Attributes = MemberAttributes.Public | MemberAttributes.Override;
179 Random rnd = new Random (pageParser.InputFile.GetHashCode ());
180 method.Statements.Add (new CodeMethodReturnStatement (new CodePrimitiveExpression (rnd.Next ())));
181 mainClass.Members.Add (method);
184 static CodeExpression GetExpressionForValueAndType (object value, Type valueType)
186 // Put short circuit types here
187 if (valueType == typeof (TimeSpan)) {
188 CodeMethodReferenceExpression mref = new CodeMethodReferenceExpression (
189 new CodeTypeReferenceExpression (typeof (TimeSpan)),
192 return new CodeMethodInvokeExpression (
194 new CodeExpression[] { new CodePrimitiveExpression (((TimeSpan) value).ToString ()) }
198 throw new HttpException (String.Format ("Unable to create assign expression for type '{0}'.", valueType));
201 static CodeAssignStatement CreatePropertyAssign (CodeExpression owner, string name, CodeExpression rhs)
203 return new CodeAssignStatement (new CodePropertyReferenceExpression (owner, name), rhs);
206 static CodeAssignStatement CreatePropertyAssign (CodeExpression owner, string name, object value)
209 if (value == null || value is string)
210 rhs = new CodePrimitiveExpression (value);
212 Type vt = value.GetType ();
215 rhs = new CodePrimitiveExpression (value);
217 rhs = GetExpressionForValueAndType (value, vt);
220 return CreatePropertyAssign (owner, name, rhs);
223 static CodeAssignStatement CreatePropertyAssign (string name, object value)
225 return CreatePropertyAssign (thisRef, name, value);
228 void AssignPropertyWithExpression <T> (CodeMemberMethod method, string name, MainDirectiveAttribute <T> value, ILocation location)
232 CodeAssignStatement assign;
233 CodeExpression rhs = null;
235 if (value.IsExpression) {
236 var pi = GetFieldOrProperty (typeof (Page), name) as PropertyInfo;
238 rhs = CompileExpression (pi, pi.PropertyType, value.UnparsedValue, false);
242 assign = CreatePropertyAssign (thisRef, name, rhs);
244 assign = CreatePropertyAssign (name, value.Value);
246 method.Statements.Add (AddLinePragma (assign, location));
249 void AddStatementsFromDirective (ControlBuilder builder, CodeMemberMethod method, ILocation location)
251 AssignPropertyWithExpression <string> (method, "ResponseEncoding", pageParser.ResponseEncoding, location);
252 AssignPropertyWithExpression <int> (method, "CodePage", pageParser.CodePage, location);
253 AssignPropertyWithExpression <int> (method, "LCID", pageParser.LCID, location);
255 string contentType = pageParser.ContentType;
256 if (contentType != null)
257 method.Statements.Add (AddLinePragma (CreatePropertyAssign ("ContentType", contentType), location));
259 string culture = pageParser.Culture;
261 method.Statements.Add (AddLinePragma (CreatePropertyAssign ("Culture", culture), location));
263 culture = pageParser.UICulture;
265 method.Statements.Add (AddLinePragma (CreatePropertyAssign ("UICulture", culture), location));
267 string errorPage = pageParser.ErrorPage;
268 if (errorPage != null)
269 method.Statements.Add (AddLinePragma (CreatePropertyAssign ("ErrorPage", errorPage), location));
271 if (pageParser.HaveTrace) {
272 CodeAssignStatement stmt = new CodeAssignStatement ();
273 stmt.Left = new CodePropertyReferenceExpression (thisRef, "TraceEnabled");
274 stmt.Right = new CodePrimitiveExpression (pageParser.Trace);
275 method.Statements.Add (AddLinePragma (stmt, location));
278 if (pageParser.TraceMode != TraceMode.Default) {
279 CodeAssignStatement stmt = new CodeAssignStatement ();
280 CodeTypeReferenceExpression tm = new CodeTypeReferenceExpression ("System.Web.TraceMode");
281 stmt.Left = new CodePropertyReferenceExpression (thisRef, "TraceModeValue");
282 stmt.Right = new CodeFieldReferenceExpression (tm, pageParser.TraceMode.ToString ());
283 method.Statements.Add (AddLinePragma (stmt, location));
286 if (pageParser.NotBuffer) {
287 CodeAssignStatement stmt = new CodeAssignStatement ();
288 stmt.Left = new CodePropertyReferenceExpression (thisRef, "Buffer");
289 stmt.Right = new CodePrimitiveExpression (false);
290 method.Statements.Add (AddLinePragma (stmt, location));
293 if (!pageParser.EnableEventValidation) {
294 CodeAssignStatement stmt = new CodeAssignStatement ();
295 CodePropertyReferenceExpression prop;
296 prop = new CodePropertyReferenceExpression (thisRef, "EnableEventValidation");
298 stmt.Right = new CodePrimitiveExpression (pageParser.EnableEventValidation);
299 method.Statements.Add (AddLinePragma (stmt, location));
302 if (pageParser.MaintainScrollPositionOnPostBack) {
303 CodeAssignStatement stmt = new CodeAssignStatement ();
304 CodePropertyReferenceExpression prop;
305 prop = new CodePropertyReferenceExpression (thisRef, "MaintainScrollPositionOnPostBack");
307 stmt.Right = new CodePrimitiveExpression (pageParser.MaintainScrollPositionOnPostBack);
308 method.Statements.Add (AddLinePragma (stmt, location));
312 protected override void AddStatementsToConstructor (CodeConstructor ctor)
314 base.AddStatementsToConstructor (ctor);
315 if (pageParser.OutputCache)
316 OutputCacheParamsBlock (ctor);
319 protected override void AddStatementsToInitMethodTop (ControlBuilder builder, CodeMemberMethod method)
321 ILocation directiveLocation = pageParser.DirectiveLocation;
322 AddStatementsFromDirective (builder, method, directiveLocation);
324 CodeArgumentReferenceExpression ctrlVar = new CodeArgumentReferenceExpression("__ctrl");
325 if (pageParser.EnableViewStateMacSet)
326 method.Statements.Add (AddLinePragma (CreatePropertyAssign (ctrlVar, "EnableViewStateMac", pageParser.EnableViewStateMacSet), directiveLocation));
328 AssignPropertyWithExpression <string> (method, "Title", pageParser.Title, directiveLocation);
329 AssignPropertyWithExpression <string> (method, "MasterPageFile", pageParser.MasterPageFile, directiveLocation);
330 AssignPropertyWithExpression <string> (method, "Theme", pageParser.Theme, directiveLocation);
332 if (pageParser.StyleSheetTheme != null)
333 method.Statements.Add (AddLinePragma (CreatePropertyAssign (ctrlVar, "StyleSheetTheme", pageParser.StyleSheetTheme), directiveLocation));
335 if (pageParser.Async != false)
336 method.Statements.Add (AddLinePragma (CreatePropertyAssign (ctrlVar, "AsyncMode", pageParser.Async), directiveLocation));
338 if (pageParser.AsyncTimeout != -1)
339 method.Statements.Add (AddLinePragma (CreatePropertyAssign (ctrlVar, "AsyncTimeout",
340 TimeSpan.FromSeconds (pageParser.AsyncTimeout)), directiveLocation));
342 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression (thisRef, "InitializeCulture");
343 method.Statements.Add (AddLinePragma (new CodeExpressionStatement (expr), directiveLocation));
346 protected override void AddStatementsToInitMethodBottom (ControlBuilder builder, CodeMemberMethod method)
348 ILocation directiveLocation = pageParser.DirectiveLocation;
349 AssignPropertyWithExpression <string> (method, "MetaDescription", pageParser.MetaDescription, directiveLocation);
350 AssignPropertyWithExpression <string> (method, "MetaKeywords", pageParser.MetaKeywords, directiveLocation);
353 protected override void PrependStatementsToFrameworkInitialize (CodeMemberMethod method)
355 base.PrependStatementsToFrameworkInitialize (method);
356 if (pageParser.StyleSheetTheme != null)
357 method.Statements.Add (CreatePropertyAssign ("StyleSheetTheme", pageParser.StyleSheetTheme));
360 protected override void AppendStatementsToFrameworkInitialize (CodeMemberMethod method)
362 base.AppendStatementsToFrameworkInitialize (method);
364 ArrayList deps = pageParser.Dependencies;
365 int depsCount = deps != null ? deps.Count : 0;
368 CodeFieldReferenceExpression fileDependencies = GetMainClassFieldReferenceExpression ("__fileDependencies");
370 method.Statements.Add (
371 new CodeMethodInvokeExpression (
373 "AddWrappedFileDependencies",
374 new CodeExpression[] {fileDependencies})
379 if (pageParser.OutputCache) {
380 CodeMethodReferenceExpression init = new CodeMethodReferenceExpression (thisRef, "InitOutputCache");
381 CodeMethodInvokeExpression invoke = new CodeMethodInvokeExpression (init, GetMainClassFieldReferenceExpression ("__outputCacheSettings"));
382 method.Statements.Add (invoke);
385 if (pageParser.ValidateRequest) {
386 CodeMethodInvokeExpression expr = new CodeMethodInvokeExpression ();
387 CodePropertyReferenceExpression prop;
388 prop = new CodePropertyReferenceExpression (thisRef, "Request");
389 expr.Method = new CodeMethodReferenceExpression (prop, "ValidateInput");
390 method.Statements.Add (expr);
394 CodeAssignStatement AssignOutputCacheParameter (CodeVariableReferenceExpression variable, string propName, object value)
396 var ret = new CodeAssignStatement ();
398 ret.Left = new CodeFieldReferenceExpression (variable, propName);
400 if (value is OutputCacheLocation)
401 ret.Right = new CodeFieldReferenceExpression (
402 new CodeTypeReferenceExpression (new CodeTypeReference (typeof (OutputCacheLocation), CodeTypeReferenceOptions.GlobalReference)),
406 ret.Right = new CodePrimitiveExpression (value);
410 void OutputCacheParamsBlock (CodeMemberMethod method)
412 var statements = new List <CodeStatement> ();
413 var localSettingsDecl = new CodeVariableDeclarationStatement (typeof (OutputCacheParameters), "outputCacheSettings");
414 var localSettings = new CodeVariableReferenceExpression ("outputCacheSettings");
416 statements.Add (localSettingsDecl);
418 new CodeAssignStatement (
420 new CodeObjectCreateExpression (typeof (OutputCacheParameters), new CodeExpression[] {})
424 TemplateParser.OutputCacheParsedParams parsed = pageParser.OutputCacheParsedParameters;
425 if ((parsed & TemplateParser.OutputCacheParsedParams.CacheProfile) != 0)
426 statements.Add (AssignOutputCacheParameter (localSettings, "CacheProfile", pageParser.OutputCacheCacheProfile));
427 statements.Add (AssignOutputCacheParameter (localSettings, "Duration", pageParser.OutputCacheDuration));
428 if ((parsed & TemplateParser.OutputCacheParsedParams.Location) != 0)
429 statements.Add (AssignOutputCacheParameter (localSettings, "Location", pageParser.OutputCacheLocation));
430 if ((parsed & TemplateParser.OutputCacheParsedParams.NoStore) != 0)
431 statements.Add (AssignOutputCacheParameter (localSettings, "NoStore", pageParser.OutputCacheNoStore));
432 if ((parsed & TemplateParser.OutputCacheParsedParams.SqlDependency) != 0)
433 statements.Add (AssignOutputCacheParameter (localSettings, "SqlDependency", pageParser.OutputCacheSqlDependency));
434 if ((parsed & TemplateParser.OutputCacheParsedParams.VaryByContentEncodings) != 0)
435 statements.Add (AssignOutputCacheParameter (localSettings, "VaryByContentEncoding", pageParser.OutputCacheVaryByContentEncodings));
436 if ((parsed & TemplateParser.OutputCacheParsedParams.VaryByControl) != 0)
437 statements.Add (AssignOutputCacheParameter (localSettings, "VaryByControl", pageParser.OutputCacheVaryByControls));
438 if ((parsed & TemplateParser.OutputCacheParsedParams.VaryByCustom) != 0)
439 statements.Add (AssignOutputCacheParameter (localSettings, "VaryByCustom", pageParser.OutputCacheVaryByCustom));
440 if ((parsed & TemplateParser.OutputCacheParsedParams.VaryByHeader) != 0)
441 statements.Add (AssignOutputCacheParameter (localSettings, "VaryByHeader", pageParser.OutputCacheVaryByHeader));
442 statements.Add (AssignOutputCacheParameter (localSettings, "VaryByParam", pageParser.OutputCacheVaryByParam));
444 CodeFieldReferenceExpression outputCacheSettings = GetMainClassFieldReferenceExpression ("__outputCacheSettings");
445 statements.Add (new CodeAssignStatement (outputCacheSettings, localSettings));
447 var cond = new CodeConditionStatement (
448 new CodeBinaryOperatorExpression (
450 CodeBinaryOperatorType.IdentityEquality,
451 new CodePrimitiveExpression (null)
453 statements.ToArray ()
456 method.Statements.Add (cond);
459 void CreateStronglyTypedProperty (Type type, string name)
464 CodeMemberProperty mprop = new CodeMemberProperty ();
466 mprop.Type = new CodeTypeReference (type);
467 mprop.Attributes = MemberAttributes.Public | MemberAttributes.New;
468 CodeExpression prop = new CodePropertyReferenceExpression (new CodeBaseReferenceExpression (), name);
469 prop = new CodeCastExpression (type, prop);
470 mprop.GetStatements.Add (new CodeMethodReturnStatement (prop));
471 if (partialClass != null)
472 partialClass.Members.Add (mprop);
474 mainClass.Members.Add (mprop);
476 AddReferencedAssembly (type.Assembly);
479 protected internal override void CreateMethods ()
481 base.CreateMethods ();
483 CreateProfileProperty ();
484 CreateStronglyTypedProperty (pageParser.MasterType, "Master");
485 CreateStronglyTypedProperty (pageParser.PreviousPageType, "PreviousPage");
486 CreateGetTypeHashCode ();
488 if (pageParser.Async)
489 CreateAsyncMethods ();
492 void CreateAsyncMethods ()
494 CodeMemberMethod method = new CodeMemberMethod ();
495 CodeParameterDeclarationExpression arg;
496 CodeMethodInvokeExpression invoke;
498 // public virtual System.IAsyncResult BeginProcessRequest(System.Web.HttpContext context, System.AsyncCallback cb, object data);
499 method.ReturnType = new CodeTypeReference (typeof (IAsyncResult));
500 method.Name = "BeginProcessRequest";
501 method.Attributes = MemberAttributes.Public;
503 arg = new CodeParameterDeclarationExpression ();
504 arg.Type = new CodeTypeReference (typeof (HttpContext));
505 arg.Name = "context";
506 method.Parameters.Add (arg);
508 arg = new CodeParameterDeclarationExpression ();
509 arg.Type = new CodeTypeReference (typeof (AsyncCallback));
511 method.Parameters.Add (arg);
513 arg = new CodeParameterDeclarationExpression ();
514 arg.Type = new CodeTypeReference (typeof (object));
516 method.Parameters.Add (arg);
518 invoke = new CodeMethodInvokeExpression (thisRef, "AsyncPageBeginProcessRequest");
519 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("context"));
520 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("cb"));
521 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("data"));
523 method.Statements.Add (new CodeMethodReturnStatement (invoke));
524 mainClass.Members.Add (method);
526 // public virtual void EndProcessRequest(System.IAsyncResult ar);
527 method = new CodeMemberMethod ();
528 method.ReturnType = new CodeTypeReference (typeof (void));
529 method.Name = "EndProcessRequest";
530 method.Attributes = MemberAttributes.Public;
532 arg = new CodeParameterDeclarationExpression ();
533 arg.Type = new CodeTypeReference (typeof (IAsyncResult));
535 method.Parameters.Add (arg);
537 invoke = new CodeMethodInvokeExpression (thisRef, "AsyncPageEndProcessRequest");
538 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("ar"));
540 method.Statements.Add (invoke);
541 mainClass.Members.Add (method);
543 // public override void ProcessRequest(System.Web.HttpContext context);
544 method = new CodeMemberMethod ();
545 method.ReturnType = new CodeTypeReference (typeof (void));
546 method.Name = "ProcessRequest";
547 method.Attributes = MemberAttributes.Public | MemberAttributes.Override;
549 arg = new CodeParameterDeclarationExpression ();
550 arg.Type = new CodeTypeReference (typeof (HttpContext));
551 arg.Name = "context";
552 method.Parameters.Add (arg);
554 invoke = new CodeMethodInvokeExpression (new CodeBaseReferenceExpression (), "ProcessRequest");
555 invoke.Parameters.Add (new CodeArgumentReferenceExpression ("context"));
557 method.Statements.Add (invoke);
558 mainClass.Members.Add (method);
561 public static Type CompilePageType (PageParser pageParser)
563 PageCompiler compiler = new PageCompiler (pageParser);
564 return compiler.GetCompiledType ();