TemplateParser.cs 18 KB

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