TemplateParser.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. //
  2. // System.Web.UI.TemplateParser
  3. //
  4. // Authors:
  5. // Duncan Mak ([email protected])
  6. // Gonzalo Paniagua Javier ([email protected])
  7. //
  8. // (C) 2002,2003 Ximian, Inc. (http://www.ximian.com)
  9. // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
  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. using System.CodeDom.Compiler;
  31. using System.Collections;
  32. using System.IO;
  33. using System.Reflection;
  34. using System.Security.Permissions;
  35. using System.Web.Compilation;
  36. using System.Web.Configuration;
  37. using System.Web.Util;
  38. namespace System.Web.UI {
  39. // CAS
  40. [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  41. [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  42. public abstract class TemplateParser : BaseParser
  43. {
  44. string inputFile;
  45. string text;
  46. string privateBinPath;
  47. Hashtable mainAttributes;
  48. ArrayList dependencies;
  49. ArrayList assemblies;
  50. Hashtable anames;
  51. ArrayList imports;
  52. ArrayList interfaces;
  53. ArrayList scripts;
  54. Type baseType;
  55. string className;
  56. RootBuilder rootBuilder;
  57. bool debug;
  58. string compilerOptions;
  59. string language;
  60. bool strictOn = false;
  61. bool explicitOn = false;
  62. bool output_cache;
  63. int oc_duration;
  64. string oc_header, oc_custom, oc_param, oc_controls;
  65. bool oc_shared;
  66. OutputCacheLocation oc_location;
  67. #if NET_2_0
  68. string src;
  69. string partialClassName;
  70. #endif
  71. Assembly srcAssembly;
  72. int appAssemblyIndex = -1;
  73. internal TemplateParser ()
  74. {
  75. imports = new ArrayList ();
  76. imports.Add ("System");
  77. imports.Add ("System.Collections");
  78. imports.Add ("System.Collections.Specialized");
  79. imports.Add ("System.Configuration");
  80. imports.Add ("System.Text");
  81. imports.Add ("System.Text.RegularExpressions");
  82. imports.Add ("System.Web");
  83. imports.Add ("System.Web.Caching");
  84. imports.Add ("System.Web.Security");
  85. imports.Add ("System.Web.SessionState");
  86. imports.Add ("System.Web.UI");
  87. imports.Add ("System.Web.UI.WebControls");
  88. imports.Add ("System.Web.UI.HtmlControls");
  89. assemblies = new ArrayList ();
  90. #if NET_2_0
  91. bool addAssembliesInBin = false;
  92. foreach (AssemblyInfo info in CompilationConfig.Assemblies) {
  93. if (info.Assembly == "*")
  94. addAssembliesInBin = true;
  95. else
  96. AddAssemblyByName (info.Assembly);
  97. }
  98. if (addAssembliesInBin)
  99. AddAssembliesInBin ();
  100. #else
  101. foreach (string a in CompilationConfig.Assemblies)
  102. AddAssemblyByName (a);
  103. if (CompilationConfig.AssembliesInBin)
  104. AddAssembliesInBin ();
  105. #endif
  106. language = CompilationConfig.DefaultLanguage;
  107. }
  108. internal void AddApplicationAssembly ()
  109. {
  110. string location = Context.ApplicationInstance.AssemblyLocation;
  111. if (location != typeof (TemplateParser).Assembly.Location) {
  112. appAssemblyIndex = assemblies.Add (location);
  113. }
  114. }
  115. protected abstract Type CompileIntoType ();
  116. internal virtual void HandleOptions (object obj)
  117. {
  118. }
  119. internal static string GetOneKey (Hashtable tbl)
  120. {
  121. foreach (object key in tbl.Keys)
  122. return key.ToString ();
  123. return null;
  124. }
  125. internal virtual void AddDirective (string directive, Hashtable atts)
  126. {
  127. if (String.Compare (directive, DefaultDirectiveName, true) == 0) {
  128. if (mainAttributes != null)
  129. ThrowParseException ("Only 1 " + DefaultDirectiveName + " is allowed");
  130. mainAttributes = atts;
  131. ProcessMainAttributes (mainAttributes);
  132. return;
  133. }
  134. int cmp = String.Compare ("Assembly", directive, true);
  135. if (cmp == 0) {
  136. string name = GetString (atts, "Name", null);
  137. string src = GetString (atts, "Src", null);
  138. if (atts.Count > 0)
  139. ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
  140. if (name == null && src == null)
  141. ThrowParseException ("You gotta specify Src or Name");
  142. if (name != null && src != null)
  143. ThrowParseException ("Src and Name cannot be used together");
  144. if (name != null) {
  145. AddAssemblyByName (name);
  146. } else {
  147. GetAssemblyFromSource (src);
  148. }
  149. return;
  150. }
  151. cmp = String.Compare ("Import", directive, true);
  152. if (cmp == 0) {
  153. string namesp = GetString (atts, "Namespace", null);
  154. if (atts.Count > 0)
  155. ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
  156. if (namesp != null && namesp != "")
  157. AddImport (namesp);
  158. return;
  159. }
  160. cmp = String.Compare ("Implements", directive, true);
  161. if (cmp == 0) {
  162. string ifacename = GetString (atts, "Interface", "");
  163. if (atts.Count > 0)
  164. ThrowParseException ("Attribute " + GetOneKey (atts) + " unknown.");
  165. Type iface = LoadType (ifacename);
  166. if (iface == null)
  167. ThrowParseException ("Cannot find type " + ifacename);
  168. if (!iface.IsInterface)
  169. ThrowParseException (iface + " is not an interface");
  170. AddInterface (iface.FullName);
  171. return;
  172. }
  173. cmp = String.Compare ("OutputCache", directive, true);
  174. if (cmp == 0) {
  175. output_cache = true;
  176. if (atts ["Duration"] == null)
  177. ThrowParseException ("The directive is missing a 'duration' attribute.");
  178. if (atts ["VaryByParam"] == null)
  179. ThrowParseException ("This directive is missing a 'VaryByParam' " +
  180. "attribute, which should be set to \"none\", \"*\", " +
  181. "or a list of name/value pairs.");
  182. foreach (DictionaryEntry entry in atts) {
  183. string key = (string) entry.Key;
  184. switch (key.ToLower ()) {
  185. case "duration":
  186. oc_duration = Int32.Parse ((string) entry.Value);
  187. if (oc_duration < 1)
  188. ThrowParseException ("The 'duration' attribute must be set " +
  189. "to a positive integer value");
  190. break;
  191. case "varybyparam":
  192. oc_param = (string) entry.Value;
  193. if (String.Compare (oc_param, "none") == 0)
  194. oc_param = null;
  195. break;
  196. case "varybyheader":
  197. oc_header = (string) entry.Value;
  198. break;
  199. case "varybycustom":
  200. oc_custom = (string) entry.Value;
  201. break;
  202. case "location":
  203. if (!(this is PageParser))
  204. goto default;
  205. try {
  206. oc_location = (OutputCacheLocation) Enum.Parse (
  207. typeof (OutputCacheLocation), (string) entry.Value, true);
  208. } catch {
  209. ThrowParseException ("The 'location' attribute is case sensitive and " +
  210. "must be one of the following values: Any, Client, " +
  211. "Downstream, Server, None, ServerAndClient.");
  212. }
  213. break;
  214. case "varybycontrol":
  215. if (this is PageParser)
  216. goto default;
  217. oc_controls = (string) entry.Value;
  218. break;
  219. case "shared":
  220. if (this is PageParser)
  221. goto default;
  222. try {
  223. oc_shared = Boolean.Parse ((string) entry.Value);
  224. } catch {
  225. ThrowParseException ("The 'shared' attribute is case sensitive" +
  226. " and must be set to 'true' or 'false'.");
  227. }
  228. break;
  229. default:
  230. ThrowParseException ("The '" + key + "' attribute is not " +
  231. "supported by the 'Outputcache' directive.");
  232. break;
  233. }
  234. }
  235. return;
  236. }
  237. ThrowParseException ("Unknown directive: " + directive);
  238. }
  239. internal Type LoadType (string typeName)
  240. {
  241. // First try loaded assemblies, then try assemblies in Bin directory.
  242. Type type = null;
  243. bool seenBin = false;
  244. Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies ();
  245. foreach (Assembly ass in assemblies) {
  246. type = ass.GetType (typeName);
  247. if (type == null)
  248. continue;
  249. if (Path.GetDirectoryName (ass.Location) != PrivateBinPath) {
  250. AddAssembly (ass, true);
  251. } else {
  252. seenBin = true;
  253. }
  254. AddDependency (ass.Location);
  255. return type;
  256. }
  257. if (seenBin)
  258. return null;
  259. // Load from bin
  260. if (!Directory.Exists (PrivateBinPath))
  261. return null;
  262. string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
  263. foreach (string s in binDlls) {
  264. Assembly binA = Assembly.LoadFrom (s);
  265. type = binA.GetType (typeName);
  266. if (type == null)
  267. continue;
  268. AddDependency (binA.Location);
  269. return type;
  270. }
  271. return null;
  272. }
  273. void AddAssembliesInBin ()
  274. {
  275. if (!Directory.Exists (PrivateBinPath))
  276. return;
  277. string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
  278. foreach (string s in binDlls) {
  279. assemblies.Add (s);
  280. }
  281. }
  282. internal virtual void AddInterface (string iface)
  283. {
  284. if (interfaces == null)
  285. interfaces = new ArrayList ();
  286. if (!interfaces.Contains (iface))
  287. interfaces.Add (iface);
  288. }
  289. internal virtual void AddImport (string namesp)
  290. {
  291. if (imports == null)
  292. imports = new ArrayList ();
  293. if (!imports.Contains (namesp))
  294. imports.Add (namesp);
  295. }
  296. internal virtual void AddSourceDependency (string filename)
  297. {
  298. if (dependencies != null && dependencies.Contains (filename)) {
  299. ThrowParseException ("Circular file references are not allowed. File: " + filename);
  300. }
  301. AddDependency (filename);
  302. }
  303. internal virtual void AddDependency (string filename)
  304. {
  305. if (filename == "")
  306. return;
  307. if (dependencies == null)
  308. dependencies = new ArrayList ();
  309. if (!dependencies.Contains (filename))
  310. dependencies.Add (filename);
  311. }
  312. internal virtual void AddAssembly (Assembly assembly, bool fullPath)
  313. {
  314. if (assembly.Location == "")
  315. return;
  316. if (anames == null)
  317. anames = new Hashtable ();
  318. string name = assembly.GetName ().Name;
  319. string loc = assembly.Location;
  320. if (fullPath) {
  321. if (!assemblies.Contains (loc)) {
  322. assemblies.Add (loc);
  323. }
  324. anames [name] = loc;
  325. anames [loc] = assembly;
  326. } else {
  327. if (!assemblies.Contains (name)) {
  328. assemblies.Add (name);
  329. }
  330. anames [name] = assembly;
  331. }
  332. }
  333. internal virtual Assembly AddAssemblyByName (string name)
  334. {
  335. if (anames == null)
  336. anames = new Hashtable ();
  337. if (anames.Contains (name)) {
  338. object o = anames [name];
  339. if (o is string)
  340. o = anames [o];
  341. return (Assembly) o;
  342. }
  343. Assembly assembly = null;
  344. Exception error = null;
  345. if (name.IndexOf (',') != -1) {
  346. try {
  347. assembly = Assembly.Load (name);
  348. } catch (Exception e) { error = e; }
  349. }
  350. if (assembly == null) {
  351. try {
  352. assembly = Assembly.LoadWithPartialName (name);
  353. } catch (Exception e) { error = e; }
  354. }
  355. if (assembly == null)
  356. ThrowParseException ("Assembly " + name + " not found", error);
  357. AddAssembly (assembly, true);
  358. return assembly;
  359. }
  360. internal virtual void ProcessMainAttributes (Hashtable atts)
  361. {
  362. atts.Remove ("Description"); // ignored
  363. #if NET_1_1
  364. atts.Remove ("CodeBehind"); // ignored
  365. #endif
  366. atts.Remove ("AspCompat"); // ignored
  367. debug = GetBool (atts, "Debug", true);
  368. compilerOptions = GetString (atts, "CompilerOptions", "");
  369. language = GetString (atts, "Language", CompilationConfig.DefaultLanguage);
  370. strictOn = GetBool (atts, "Strict", CompilationConfig.Strict);
  371. explicitOn = GetBool (atts, "Explicit", CompilationConfig.Explicit);
  372. string inherits = GetString (atts, "Inherits", null);
  373. #if NET_2_0
  374. // In ASP 2, the source file is actually integrated with
  375. // the generated file via the use of partial classes. This
  376. // means that the code file has to be confirmed, but not
  377. // used at this point.
  378. src = GetString (atts, "CodeFile", null);
  379. if (src != null && inherits != null) {
  380. // Make sure the source exists
  381. src = UrlUtils.Combine (BaseVirtualDir, src);
  382. string realPath = MapPath (src, false);
  383. if (!File.Exists (realPath))
  384. ThrowParseException ("File " + src + " not found");
  385. // Verify that the inherits is a valid identify not a
  386. // fully-qualified name.
  387. if (!CodeGenerator.IsValidLanguageIndependentIdentifier (inherits))
  388. ThrowParseException (String.Format ("'{0}' is not valid for 'inherits'", inherits));
  389. // We are going to create a partial class that shares
  390. // the same name as the inherits tag, so reset the
  391. // name. The base type is changed because it is the
  392. // code file's responsibilty to extend the classes
  393. // needed.
  394. partialClassName = inherits;
  395. // Add the code file as an option to the
  396. // compiler. This lets both files be compiled at once.
  397. compilerOptions += " " + realPath;
  398. } else if (inherits != null) {
  399. // We just set the inherits directly because this is a
  400. // Single-Page model.
  401. SetBaseType (inherits);
  402. }
  403. #else
  404. string src = GetString (atts, "Src", null);
  405. if (src != null)
  406. srcAssembly = GetAssemblyFromSource (src);
  407. if (inherits != null)
  408. SetBaseType (inherits);
  409. className = GetString (atts, "ClassName", null);
  410. if (className != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (className))
  411. ThrowParseException (String.Format ("'{0}' is not valid for 'className'", className));
  412. #endif
  413. if (atts.Count > 0)
  414. ThrowParseException ("Unknown attribute: " + GetOneKey (atts));
  415. }
  416. internal void SetBaseType (string type)
  417. {
  418. if (type == DefaultBaseTypeName)
  419. return;
  420. Type parent = null;
  421. if (srcAssembly != null)
  422. parent = srcAssembly.GetType (type);
  423. if (parent == null)
  424. parent = LoadType (type);
  425. if (parent == null)
  426. ThrowParseException ("Cannot find type " + type);
  427. if (!DefaultBaseType.IsAssignableFrom (parent))
  428. ThrowParseException ("The parent type does not derive from " + DefaultBaseType);
  429. baseType = parent;
  430. }
  431. Assembly GetAssemblyFromSource (string vpath)
  432. {
  433. vpath = UrlUtils.Combine (BaseVirtualDir, vpath);
  434. string realPath = MapPath (vpath, false);
  435. if (!File.Exists (realPath))
  436. ThrowParseException ("File " + vpath + " not found");
  437. AddSourceDependency (realPath);
  438. CompilerResults result = CachingCompiler.Compile (language, realPath, realPath, assemblies);
  439. if (result.NativeCompilerReturnValue != 0) {
  440. StreamReader reader = new StreamReader (realPath);
  441. throw new CompilationException (realPath, result.Errors, reader.ReadToEnd ());
  442. }
  443. AddAssembly (result.CompiledAssembly, true);
  444. return result.CompiledAssembly;
  445. }
  446. internal abstract Type DefaultBaseType { get; }
  447. internal abstract string DefaultBaseTypeName { get; }
  448. internal abstract string DefaultDirectiveName { get; }
  449. internal string InputFile
  450. {
  451. get { return inputFile; }
  452. set { inputFile = value; }
  453. }
  454. #if NET_2_0
  455. internal bool IsPartial
  456. {
  457. get { return src != null; }
  458. }
  459. internal string PartialClassName
  460. {
  461. get { return partialClassName; }
  462. }
  463. #endif
  464. internal string Text
  465. {
  466. get { return text; }
  467. set { text = value; }
  468. }
  469. internal Type BaseType
  470. {
  471. get {
  472. if (baseType == null)
  473. baseType = DefaultBaseType;
  474. return baseType;
  475. }
  476. }
  477. internal string ClassName {
  478. get {
  479. if (className != null)
  480. return className;
  481. className = Path.GetFileName (inputFile).Replace ('.', '_');
  482. className = className.Replace ('-', '_');
  483. className = className.Replace (' ', '_');
  484. if (Char.IsDigit(className[0])) {
  485. className = "_" + className;
  486. }
  487. return className;
  488. }
  489. }
  490. internal string PrivateBinPath {
  491. get {
  492. if (privateBinPath != null)
  493. return privateBinPath;
  494. AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
  495. privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
  496. return privateBinPath;
  497. }
  498. }
  499. internal ArrayList Scripts {
  500. get {
  501. if (scripts == null)
  502. scripts = new ArrayList ();
  503. return scripts;
  504. }
  505. }
  506. internal ArrayList Imports {
  507. get { return imports; }
  508. }
  509. internal ArrayList Assemblies {
  510. get {
  511. if (appAssemblyIndex != -1) {
  512. object o = assemblies [appAssemblyIndex];
  513. assemblies.RemoveAt (appAssemblyIndex);
  514. assemblies.Add (o);
  515. appAssemblyIndex = -1;
  516. }
  517. return assemblies;
  518. }
  519. }
  520. internal ArrayList Interfaces {
  521. get { return interfaces; }
  522. }
  523. internal RootBuilder RootBuilder {
  524. get { return rootBuilder; }
  525. set { rootBuilder = value; }
  526. }
  527. internal ArrayList Dependencies {
  528. get { return dependencies; }
  529. set { dependencies = value; }
  530. }
  531. internal string CompilerOptions {
  532. get { return compilerOptions; }
  533. }
  534. internal string Language {
  535. get { return language; }
  536. }
  537. internal bool StrictOn {
  538. get { return strictOn; }
  539. }
  540. internal bool ExplicitOn {
  541. get { return explicitOn; }
  542. }
  543. internal bool Debug {
  544. get { return debug; }
  545. }
  546. internal bool OutputCache {
  547. get { return output_cache; }
  548. }
  549. internal int OutputCacheDuration {
  550. get { return oc_duration; }
  551. }
  552. internal string OutputCacheVaryByHeader {
  553. get { return oc_header; }
  554. }
  555. internal string OutputCacheVaryByCustom {
  556. get { return oc_custom; }
  557. }
  558. internal string OutputCacheVaryByControls {
  559. get { return oc_controls; }
  560. }
  561. internal bool OutputCacheShared {
  562. get { return oc_shared; }
  563. }
  564. internal OutputCacheLocation OutputCacheLocation {
  565. get { return oc_location; }
  566. }
  567. internal string OutputCacheVaryByParam {
  568. get { return oc_param; }
  569. }
  570. #if NET_2_0
  571. internal PagesSection PagesConfig {
  572. get {
  573. return WebConfigurationManager.GetSection ("system.web/pages") as PagesSection;
  574. }
  575. }
  576. #else
  577. internal PagesConfiguration PagesConfig {
  578. get { return PagesConfiguration.GetInstance (Context); }
  579. }
  580. #endif
  581. }
  582. }