PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/4.0/Source/Core/Reflection/Inclusions.CLR.cs

#
C# | 398 lines | 254 code | 74 blank | 70 comment | 32 complexity | f9cbb34187804dd3ef938f263809a558 MD5 | raw file
Possible License(s): CPL-1.0, GPL-2.0, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. /*
  2. Copyright (c) 2006 Tomas Matousek.
  3. The use and distribution terms for this software are contained in the file named License.txt,
  4. which can be found in the root of the Phalanger distribution. By using this software
  5. in any fashion, you are agreeing to be bound by the terms of this license.
  6. You must not remove this notice from this software.
  7. */
  8. using System;
  9. using System.IO;
  10. using System.Collections.Generic;
  11. using System.Text;
  12. using System.Diagnostics;
  13. using System.Text.RegularExpressions;
  14. using System.CodeDom.Compiler;
  15. using System.CodeDom;
  16. using PHP.Core;
  17. using PHP.Core.AST;
  18. using PHP.Core.Parsers;
  19. using PHP.Core.Emit;
  20. namespace PHP.Core.Reflection
  21. {
  22. internal enum Characteristic
  23. {
  24. StaticArgEvaluated,
  25. StaticArgReplaced,
  26. StaticAutoInclusion,
  27. Dynamic
  28. }
  29. #region InclusionMapping
  30. /// <summary>
  31. /// Defines an inclusion mapping.
  32. /// </summary>
  33. [Serializable]
  34. public struct InclusionMapping
  35. {
  36. /// <summary>
  37. /// Name identifying the mapping.
  38. /// </summary>
  39. public string Name { get { return name; } }
  40. private string name;
  41. /// <summary>
  42. /// Replacement.
  43. /// </summary>
  44. public string Replacement { get { return replacement; } }
  45. private string/*!*/ replacement;
  46. /// <summary>
  47. /// Pattern.
  48. /// </summary>
  49. public Regex Pattern { get { return pattern; } }
  50. private Regex/*!*/ pattern;
  51. /// <summary>
  52. /// Group name interpreted as the source root of application, <see cref="ApplicationConfiguration.CompilerSection.SourceRoot"/>.
  53. /// </summary>
  54. private const string SourceRootGroupName = "${SourceRoot}";
  55. /// <summary>
  56. /// Creates an inclusion mapping.
  57. /// </summary>
  58. /// <exception cref="ArgumentException"><paramref name="pattern"/> is not a valid regular expression pattern.</exception>
  59. /// <exception cref="ArgumentNullException"><paramref name="pattern"/> or <paramref name="replacement"/> is a <B>null</B> reference.</exception>
  60. public InclusionMapping(string/*!*/ pattern, string/*!*/ replacement, string name)
  61. {
  62. if (pattern == null)
  63. throw new ArgumentNullException("pattern");
  64. if (replacement == null)
  65. throw new ArgumentNullException("replacement");
  66. this.pattern = new Regex(pattern, RegexOptions.IgnoreCase);
  67. this.name = name;
  68. this.replacement = replacement;
  69. }
  70. /// <summary>
  71. /// Translates expression (a parameter of include/require) according to the pattern specified in the configuration.
  72. /// </summary>
  73. /// <param name="expression">The expression to be translated via regexp pattern.</param>
  74. /// <param name="mappings">A list of mappings.</param>
  75. /// <param name="sourceRoot">The <see cref="ApplicationConfiguration.CompilerSection.SourceRoot"/> used to patch <see cref="InclusionMapping.Replacement"/> string.</param>
  76. internal static string TranslateExpression(IEnumerable<InclusionMapping>/*!*/ mappings, string/*!*/ expression, string sourceRoot)
  77. {
  78. Debug.Assert(mappings != null && expression != null);
  79. string trimmed_expression = expression.Trim();
  80. foreach (InclusionMapping mapping in mappings)
  81. {
  82. // the regex not empty => perform translation:
  83. Match m = mapping.Pattern.Match(trimmed_expression);
  84. // regex matches:
  85. if (m.Success)
  86. return m.Result(mapping.Replacement.Replace(SourceRootGroupName, sourceRoot));
  87. }
  88. // no regex does match:
  89. return null;
  90. }
  91. }
  92. #endregion
  93. #region InclusionGraphBuilder
  94. internal sealed class InclusionGraphBuilder : IDisposable
  95. {
  96. public CompilationContext/*!*/ Context { get { return context; } }
  97. private readonly CompilationContext/*!*/ context;
  98. public Dictionary<PhpSourceFile, CompilationUnit> Nodes { get { return nodes; } }
  99. private readonly Dictionary<PhpSourceFile, CompilationUnit> nodes = new Dictionary<PhpSourceFile, CompilationUnit>();
  100. public List<StaticInclusion> PendingInclusions { get { return pendingInclusions; } }
  101. private readonly List<StaticInclusion> pendingInclusions = new List<StaticInclusion>();
  102. public InclusionGraphBuilder(CompilationContext/*!*/ context)
  103. {
  104. this.context = context;
  105. Statistics.Inclusions.InitializeGraph();
  106. }
  107. #region Graph Building Operations
  108. internal void NodeAdded(CompilationUnit/*!*/ compilationUnit)
  109. {
  110. Statistics.Inclusions.AddNode(compilationUnit);
  111. }
  112. internal void EdgeAdded(StaticInclusion/*!*/ staticInclusion)
  113. {
  114. Statistics.Inclusions.AddEdge(staticInclusion);
  115. }
  116. public void Dispose()
  117. {
  118. Statistics.Inclusions.BakeGraph();
  119. }
  120. #endregion
  121. public bool AnalyzeDfsTree(PhpSourceFile/*!*/ rootSourceFile)
  122. {
  123. CompilationUnit root = GetNode(rootSourceFile);
  124. ScriptCompilationUnit rootScript = root as ScriptCompilationUnit;
  125. if (rootScript != null && rootScript.State == CompilationUnit.States.Initial)
  126. {
  127. Analyzer analyzer = null;
  128. try
  129. {
  130. // builds the tree of parsed units via DFS:
  131. ProcessNode(rootScript);
  132. // finishes pending inclusions via MFP:
  133. ProcessPendingInclusions();
  134. analyzer = new Analyzer(context);
  135. // pre-analysis:
  136. rootScript.PreAnalyzeRecursively(analyzer);
  137. // member analysis:
  138. rootScript.AnalyzeMembersRecursively(analyzer);
  139. if (context.Errors.AnyFatalError) return false;
  140. // full analysis:
  141. rootScript.AnalyzeRecursively(analyzer);
  142. if (context.Errors.AnyFatalError) return false;
  143. // perform post analysis:
  144. analyzer.PostAnalyze();
  145. if (context.Errors.AnyError) return false;
  146. // TODO:
  147. // define constructed types:
  148. analyzer.DefineConstructedTypeBuilders();
  149. }
  150. catch (CompilerException)
  151. {
  152. root.State = CompilationUnit.States.Erroneous;
  153. return false;
  154. }
  155. if (context.Errors.AnyError) return false;
  156. }
  157. else if (root.State != CompilationUnit.States.Analyzed && root.State != CompilationUnit.States.Reflected)
  158. {
  159. return false;
  160. }
  161. return true;
  162. }
  163. public void EmitAllUnits(CodeGenerator/*!*/ codeGenerator)
  164. {
  165. #if DEBUG
  166. Console.WriteLine("Generating code ...");
  167. #endif
  168. foreach (ScriptCompilationUnit unit in SelectNonReflectedUnits(nodes.Values))
  169. {
  170. Debug.WriteLine("IG", "DefineBuilders: " + unit.SourceUnit.SourceFile);
  171. unit.DefineBuilders(context);
  172. }
  173. foreach (ScriptCompilationUnit unit in SelectNonReflectedUnits(nodes.Values))
  174. {
  175. Debug.WriteLine("IG", "Emit: " + unit.SourceUnit.SourceFile);
  176. unit.Emit(codeGenerator);
  177. }
  178. foreach (ScriptCompilationUnit unit in SelectNonReflectedUnits(nodes.Values))
  179. {
  180. Debug.WriteLine("IG", "Bake: " + unit.SourceUnit.SourceFile);
  181. unit.Bake();
  182. }
  183. foreach (ScriptCompilationUnit unit in SelectNonReflectedUnits(nodes.Values))
  184. {
  185. Debug.WriteLine("IG", "Persist: " + unit.SourceUnit.SourceFile);
  186. codeGenerator.Context.Manager.Persist(unit, codeGenerator.Context);
  187. }
  188. }
  189. /// <summary>
  190. /// Selects only units that are in other than 'Reflected' state. This prevents us from
  191. /// trying to build 'Reflected' units (which is of course impossible)
  192. /// </summary>
  193. private IEnumerable<ScriptCompilationUnit> SelectNonReflectedUnits(Dictionary<PhpSourceFile,
  194. CompilationUnit>.ValueCollection values)
  195. {
  196. foreach (CompilationUnit unit in values)
  197. if (unit.State != CompilationUnit.States.Reflected) yield return (ScriptCompilationUnit)unit;
  198. }
  199. public void CleanAllUnits(CompilationContext/*!*/ context, bool successful)
  200. {
  201. foreach (CompilationUnit unit in nodes.Values)
  202. unit.CleanUp(context, successful);
  203. }
  204. /// <summary>
  205. /// Gets the node of the graph associated with the specified source file.
  206. /// First, look up the table of processed nodes.
  207. /// If not there, check compiled units maintained by the manager.
  208. /// If it is not found in the manager's cache the source file is locked so that other compilers will
  209. /// wait until we finish compilation of the node. The new node is created if the compilation unit doesn't exist for it.
  210. /// </summary>
  211. internal CompilationUnit GetNode(PhpSourceFile/*!*/ sourceFile)
  212. {
  213. CompilationUnit result;
  214. if (!nodes.TryGetValue(sourceFile, out result))
  215. {
  216. ScriptModule module;
  217. module = (ScriptModule)context.Manager.LockForCompiling(sourceFile, context);
  218. if (module != null)
  219. {
  220. result = module.CompilationUnit;
  221. }
  222. else
  223. {
  224. ScriptCompilationUnit scriptResult = new ScriptCompilationUnit();
  225. scriptResult.SourceUnit = new SourceFileUnit(scriptResult, sourceFile, context.Config.Globalization.PageEncoding);
  226. result = scriptResult;
  227. }
  228. nodes.Add(sourceFile, result);
  229. NodeAdded(result);
  230. }
  231. return result;
  232. }
  233. private void ProcessNode(ScriptCompilationUnit/*!*/ node)
  234. {
  235. Debug.Assert(node.State == CompilationUnit.States.Initial);
  236. // parses the unit and fills its tables:
  237. node.Parse(context);
  238. // resolves outgoing edges:
  239. node.ResolveInclusions(this);
  240. // follow DFS tree edges:
  241. foreach (StaticInclusion edge in node.Inclusions)
  242. {
  243. switch (edge.Includee.State)
  244. {
  245. case CompilationUnit.States.Initial:
  246. Debug.Assert(edge.Includee is ScriptCompilationUnit);
  247. // recursive descent:
  248. ProcessNode((ScriptCompilationUnit)edge.Includee); // TODO: consider!
  249. node.MergeTables(edge);
  250. break;
  251. case CompilationUnit.States.Parsed:
  252. // edge closing a cycle:
  253. pendingInclusions.Add(edge);
  254. break;
  255. case CompilationUnit.States.Processed:
  256. // transverse edge to already processed subtree:
  257. node.MergeTables(edge);
  258. break;
  259. case CompilationUnit.States.Compiled:
  260. // descent edge to the compiled node:
  261. edge.Includee.Reflect();
  262. node.MergeTables(edge);
  263. break;
  264. case CompilationUnit.States.Analyzed:
  265. case CompilationUnit.States.Reflected:
  266. // descent or transverse edge to already analyzed or compiled and reflected node:
  267. node.MergeTables(edge);
  268. break;
  269. default:
  270. Debug.Fail("Unexpected CU state");
  271. throw null;
  272. }
  273. }
  274. node.State = CompilationUnit.States.Processed;
  275. }
  276. /// <summary>
  277. /// Minimal fixpoint algorithm.
  278. /// </summary>
  279. private void ProcessPendingInclusions()
  280. {
  281. while (pendingInclusions.Count > 0)
  282. {
  283. StaticInclusion inclusion = pendingInclusions[pendingInclusions.Count - 1];
  284. pendingInclusions.RemoveAt(pendingInclusions.Count - 1);
  285. Debug.Assert(inclusion.Includer.State == CompilationUnit.States.Processed);
  286. Debug.Assert(inclusion.Includee.State == CompilationUnit.States.Processed);
  287. if (inclusion.Includer.MergeTables(inclusion) > 0)
  288. {
  289. foreach (StaticInclusion incoming in inclusion.Includer.Includers)
  290. pendingInclusions.Add(incoming);
  291. }
  292. }
  293. }
  294. }
  295. #endregion
  296. #region StaticInclusion
  297. public sealed class StaticInclusion
  298. {
  299. public ScriptCompilationUnit/*!*/ Includer { get { return includer; } }
  300. private readonly ScriptCompilationUnit/*!*/ includer;
  301. public CompilationUnit/*!*/ Includee { get { return includee; } }
  302. private readonly CompilationUnit/*!*/ includee;
  303. public Scope Scope { get { return scope; } }
  304. private readonly Scope scope;
  305. public InclusionTypes InclusionType { get { return inclusionType; } }
  306. private readonly InclusionTypes inclusionType;
  307. public bool IsConditional { get { return isConditional; } }
  308. private bool isConditional;
  309. public StaticInclusion(ScriptCompilationUnit/*!*/ includer, CompilationUnit/*!*/ includee, Scope scope,
  310. bool isConditional, InclusionTypes inclusionType)
  311. {
  312. this.scope = scope;
  313. this.inclusionType = inclusionType;
  314. this.includee = includee;
  315. this.includer = includer;
  316. this.isConditional = isConditional;
  317. }
  318. }
  319. #endregion
  320. }