2 // System.Web.Compilation.BaseCompiler
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (c) Copyright 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.
33 using System.CodeDom.Compiler;
34 using System.Collections;
35 using System.Reflection;
38 using System.Web.Configuration;
41 namespace System.Web.Compilation
43 abstract class BaseCompiler
46 static BindingFlags replaceableFlags = BindingFlags.Public | BindingFlags.NonPublic |
47 BindingFlags.Instance;
50 TemplateParser parser;
51 CodeDomProvider provider;
52 ICodeCompiler compiler;
55 CompilerParameters compilerParameters;
57 bool isRebuilding = false;
58 protected Hashtable partialNameOverride = new Hashtable();
60 protected CodeTypeDeclaration mainClass;
61 protected CodeTypeReferenceExpression mainClassExpr;
62 protected static CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression ();
64 protected BaseCompiler (TemplateParser parser)
66 compilerParameters = new CompilerParameters ();
72 unit = new CodeCompileUnit ();
74 if (parser.IsPartial) {
75 mainNS = new CodeNamespace ();
76 mainClass = new CodeTypeDeclaration (parser.PartialClassName);
77 mainClass.IsPartial = true;
78 mainClassExpr = new CodeTypeReferenceExpression (parser.PartialClassName);
81 mainNS = new CodeNamespace ("ASP");
82 mainClass = new CodeTypeDeclaration (parser.ClassName);
83 mainClass.BaseTypes.Add (new CodeTypeReference (parser.BaseType.FullName));
84 mainClassExpr = new CodeTypeReferenceExpression ("ASP." + parser.ClassName);
88 unit.Namespaces.Add (mainNS);
89 mainClass.TypeAttributes = TypeAttributes.Public;
90 mainNS.Types.Add (mainClass);
92 foreach (object o in parser.Imports) {
94 mainNS.Imports.Add (new CodeNamespaceImport ((string) o));
97 if (parser.Assemblies != null) {
98 foreach (object o in parser.Assemblies) {
100 unit.ReferencedAssemblies.Add ((string) o);
104 // Late-bound generators specifics (as for MonoBASIC/VB.NET)
105 unit.UserData["RequireVariableDeclaration"] = parser.ExplicitOn;
106 unit.UserData["AllowLateBound"] = !parser.StrictOn;
109 AddClassAttributes ();
110 CreateStaticFields ();
111 AddApplicationAndSessionObjects ();
113 CreateConstructor (null, null);
116 protected virtual void CreateStaticFields ()
118 CodeMemberField fld = new CodeMemberField (typeof (bool), "__intialized");
119 fld.Attributes = MemberAttributes.Private | MemberAttributes.Static;
120 fld.InitExpression = new CodePrimitiveExpression (false);
121 mainClass.Members.Add (fld);
124 protected virtual void CreateConstructor (CodeStatementCollection localVars,
125 CodeStatementCollection trueStmt)
127 CodeConstructor ctor = new CodeConstructor ();
128 ctor.Attributes = MemberAttributes.Public;
129 mainClass.Members.Add (ctor);
131 if (localVars != null)
132 ctor.Statements.AddRange (localVars);
134 CodeTypeReferenceExpression r;
136 if (parser.IsPartial)
137 r = new CodeTypeReferenceExpression (mainClass.Name);
140 r = new CodeTypeReferenceExpression (mainNS.Name + "." + mainClass.Name);
141 CodeFieldReferenceExpression intialized;
142 intialized = new CodeFieldReferenceExpression (r, "__intialized");
144 CodeBinaryOperatorExpression bin;
145 bin = new CodeBinaryOperatorExpression (intialized,
146 CodeBinaryOperatorType.ValueEquality,
147 new CodePrimitiveExpression (false));
149 CodeAssignStatement assign = new CodeAssignStatement (intialized,
150 new CodePrimitiveExpression (true));
152 CodeConditionStatement cond = new CodeConditionStatement (bin, assign);
153 if (trueStmt != null)
154 cond.TrueStatements.AddRange (trueStmt);
156 ctor.Statements.Add (cond);
161 if (parser.Scripts == null || parser.Scripts.Count == 0)
164 foreach (object o in parser.Scripts) {
166 mainClass.Members.Add (new CodeSnippetTypeMember ((string) o));
170 protected virtual void CreateMethods ()
174 protected virtual void AddInterfaces ()
176 if (parser.Interfaces == null)
179 foreach (object o in parser.Interfaces) {
181 mainClass.BaseTypes.Add (new CodeTypeReference ((string) o));
185 protected virtual void AddClassAttributes ()
189 protected virtual void AddApplicationAndSessionObjects ()
193 /* Utility methods for <object> stuff */
194 protected void CreateApplicationOrSessionPropertyForObject (Type type,
199 /* if isApplication this generates (the 'cachedapp' field is created earlier):
200 private MyNS.MyClass app {
202 if ((this.cachedapp == null)) {
203 this.cachedapp = ((MyNS.MyClass)
204 (this.Application.StaticObjects.GetObject("app")));
206 return this.cachedapp;
210 else, this is for Session:
211 private MyNS.MyClass ses {
213 return ((MyNS.MyClass) (this.Session.StaticObjects.GetObject("ses")));
219 CodeExpression result = null;
221 CodeMemberProperty prop = new CodeMemberProperty ();
222 prop.Type = new CodeTypeReference (type);
223 prop.Name = propName;
225 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
227 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
229 CodePropertyReferenceExpression p1;
231 p1 = new CodePropertyReferenceExpression (thisRef, "Application");
233 p1 = new CodePropertyReferenceExpression (thisRef, "Session");
235 CodePropertyReferenceExpression p2;
236 p2 = new CodePropertyReferenceExpression (p1, "StaticObjects");
238 CodeMethodReferenceExpression getobject;
239 getobject = new CodeMethodReferenceExpression (p2, "GetObject");
241 CodeMethodInvokeExpression invoker;
242 invoker = new CodeMethodInvokeExpression (getobject,
243 new CodePrimitiveExpression (propName));
245 CodeCastExpression cast = new CodeCastExpression (prop.Type, invoker);
248 CodeFieldReferenceExpression field;
249 field = new CodeFieldReferenceExpression (thisRef, "cached" + propName);
251 CodeConditionStatement stmt = new CodeConditionStatement();
252 stmt.Condition = new CodeBinaryOperatorExpression (field,
253 CodeBinaryOperatorType.IdentityEquality,
254 new CodePrimitiveExpression (null));
256 CodeAssignStatement assign = new CodeAssignStatement ();
259 stmt.TrueStatements.Add (assign);
260 prop.GetStatements.Add (stmt);
266 prop.GetStatements.Add (new CodeMethodReturnStatement (result));
267 mainClass.Members.Add (prop);
270 protected string CreateFieldForObject (Type type, string name)
272 string fieldName = "cached" + name;
273 CodeMemberField f = new CodeMemberField (type, fieldName);
274 f.Attributes = MemberAttributes.Private;
275 mainClass.Members.Add (f);
279 protected void CreatePropertyForObject (Type type, string propName, string fieldName, bool isPublic)
281 CodeFieldReferenceExpression field = new CodeFieldReferenceExpression (thisRef, fieldName);
282 CodeMemberProperty prop = new CodeMemberProperty ();
283 prop.Type = new CodeTypeReference (type);
284 prop.Name = propName;
286 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
288 prop.Attributes = MemberAttributes.Private | MemberAttributes.Final;
290 CodeConditionStatement stmt = new CodeConditionStatement();
291 stmt.Condition = new CodeBinaryOperatorExpression (field,
292 CodeBinaryOperatorType.IdentityEquality,
293 new CodePrimitiveExpression (null));
295 CodeObjectCreateExpression create = new CodeObjectCreateExpression (prop.Type);
296 stmt.TrueStatements.Add (new CodeAssignStatement (field, create));
297 prop.GetStatements.Add (stmt);
298 prop.GetStatements.Add (new CodeMethodReturnStatement (field));
300 mainClass.Members.Add (prop);
304 void CheckCompilerErrors (CompilerResults results)
306 if (results.NativeCompilerReturnValue == 0)
309 StringWriter writer = new StringWriter();
310 provider.CreateGenerator().GenerateCodeFromCompileUnit (unit, writer, null);
311 throw new CompilationException (parser.InputFile, results.Errors, writer.ToString ());
314 protected string DynamicDir ()
316 return AppDomain.CurrentDomain.SetupInformation.DynamicBase;
319 public virtual Type GetCompiledType ()
321 Type type = CachingCompiler.GetTypeFromCache (parser.InputFile);
326 string lang = parser.Language;
327 #if CONFIGURATION_2_0
328 CompilationSection config = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
329 Compiler comp = config.Compilers[lang];
330 provider = comp.Provider;
332 string compilerOptions = comp.CompilerOptions;
333 int warningLevel = comp.WarningLevel;
335 CompilationConfiguration config;
337 config = CompilationConfiguration.GetInstance (parser.Context);
338 provider = config.GetProvider (lang);
340 string compilerOptions = config.GetCompilerOptions (lang);
341 int warningLevel = config.GetWarningLevel (lang);
343 if (provider == null)
344 throw new HttpException ("Configuration error. Language not supported: " +
347 compiler = provider.CreateCompiler ();
350 compilerParameters.IncludeDebugInformation = parser.Debug;
351 compilerParameters.CompilerOptions = compilerOptions + " " + parser.CompilerOptions;
353 compilerParameters.WarningLevel = warningLevel;
354 bool keepFiles = (Environment.GetEnvironmentVariable ("MONO_ASPNET_NODELETE") != null);
356 string tempdir = config.TempDirectory;
357 if (tempdir == null || tempdir == "")
358 tempdir = DynamicDir ();
360 TempFileCollection tempcoll = new TempFileCollection (tempdir, keepFiles);
361 compilerParameters.TempFiles = tempcoll;
362 string dllfilename = Path.GetFileName (tempcoll.AddExtension ("dll", true));
363 compilerParameters.OutputAssembly = Path.Combine (DynamicDir (), dllfilename);
365 CompilerResults results = CachingCompiler.Compile (this);
366 CheckCompilerErrors (results);
367 Assembly assembly = results.CompiledAssembly;
368 if (assembly == null) {
369 if (!File.Exists (compilerParameters.OutputAssembly))
370 throw new CompilationException (parser.InputFile, results.Errors,
371 "No assembly returned after compilation!?");
373 assembly = Assembly.LoadFrom (compilerParameters.OutputAssembly);
376 results.TempFiles.Delete ();
377 Type mainClassType = assembly.GetType (mainClassExpr.Type.BaseType, true);
380 if (parser.IsPartial) {
381 // With the partial classes, we need to make sure we
382 // don't have any methods that should have not been
383 // created (because they are accessible from the base
384 // types). We cannot do this normally because the
385 // codebehind file is actually a partial class and we
386 // have no way of identifying the partial class' base
388 if (!isRebuilding && CheckPartialBaseType (mainClassType)) {
390 parser.RootBuilder.ResetState ();
391 return GetCompiledType ();
396 return mainClassType;
400 internal bool IsRebuildingPartial
402 get { return isRebuilding; }
405 internal bool CheckPartialBaseType (Type type)
407 // Get the base type. If we don't have any (bad thing), we
408 // don't need to replace ourselves. Also check for the
409 // core file, since that won't have any either.
410 Type baseType = type.BaseType;
411 if (baseType == null || baseType == typeof(System.Web.UI.Page))
414 bool rebuild = false;
416 if (CheckPartialBaseFields (type, baseType))
419 if (CheckPartialBaseProperties (type, baseType))
425 internal bool CheckPartialBaseFields (Type type, Type baseType)
427 bool rebuild = false;
429 foreach (FieldInfo baseInfo in baseType.GetFields (replaceableFlags)) {
430 if (baseInfo.IsPrivate)
433 FieldInfo typeInfo = type.GetField (baseInfo.Name, replaceableFlags);
435 if (typeInfo != null && typeInfo.DeclaringType == type) {
436 partialNameOverride [typeInfo.Name] = true;
444 internal bool CheckPartialBaseProperties (Type type, Type baseType)
446 bool rebuild = false;
448 foreach (PropertyInfo baseInfo in baseType.GetProperties ()) {
449 PropertyInfo typeInfo = type.GetProperty (baseInfo.Name);
451 if (typeInfo != null && typeInfo.DeclaringType == type) {
452 partialNameOverride [typeInfo.Name] = true;
461 internal CompilerParameters CompilerParameters {
462 get { return compilerParameters; }
465 internal CodeCompileUnit Unit {
469 internal virtual ICodeCompiler Compiler {
470 get { return compiler; }
473 internal TemplateParser Parser {
474 get { return parser; }