PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/Microsoft.Scripting/Actions/StandardRule.cs

http://ironlua.googlecode.com/
C# | 542 lines | 340 code | 76 blank | 126 comment | 72 complexity | 4d2c339741431a1ace8d87fb5527c68e MD5 | raw file
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Microsoft Public License. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Microsoft Public License, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Reflection.Emit;
  18. using System.Reflection;
  19. using System.Diagnostics;
  20. using Microsoft.Scripting.Generation;
  21. using Microsoft.Scripting.Ast;
  22. using Microsoft.Scripting.Runtime;
  23. using Microsoft.Scripting.Utils;
  24. using Microsoft.Contracts;
  25. namespace Microsoft.Scripting.Actions {
  26. using Ast = Microsoft.Scripting.Ast.Ast;
  27. /// <summary>
  28. /// Base class for all rules.
  29. ///
  30. /// A rule consists of a test and a target. The DLR compiles sets of rules
  31. /// into dynamic sites providing fast dispatch to commonly invoked functionality.
  32. /// </summary>
  33. public abstract class StandardRule {
  34. internal Expression _test; // the test that determines if the rule is applicable for its parameters
  35. internal Expression _target; // the target that executes if the rule is true
  36. internal Expression[] _parameters; // the parameters which the rule is processing
  37. internal List<object> _templateData; // the templated parameters for this rule
  38. internal List<Function<bool>> _validators; // the list of validates which indicate when the rule is no longer valid
  39. private bool _error; // true if the rule represents an error
  40. // TODO revisit these fields and their uses when CodeBlock moves down
  41. internal Variable[] _paramVariables; // TODO: Remove me when we can refer to params as expressions
  42. internal List<Variable> _temps; // TODO: Remove me when ASTs can have free-floating variables
  43. internal Dictionary<Variable, VariableReference> _references; // TODO: Remove me when the above 2 are gone
  44. private bool _canInterpretTarget = true; // true if we can interpret this rule
  45. internal StandardRule() { }
  46. /// <summary>
  47. /// An expression that should return true iff Target should be executed
  48. /// </summary>
  49. public Expression Test {
  50. get { return _test; }
  51. set { _test = value; }
  52. }
  53. /// <summary>
  54. /// The code to execute if the Test is true.
  55. /// </summary>
  56. public Expression Target {
  57. get { return _target; }
  58. set { _target = value; }
  59. }
  60. /// <summary>
  61. /// Adds a validation delegate which determines if the rule is still valid.
  62. ///
  63. /// A validator provides a dynamic test that can invalidate a rule at runtime.
  64. /// The definition of an invalid rule is one whose Test will always return false.
  65. /// In theory a set of validators is not needed as this could be encoded in the
  66. /// test itself; however, in practice it is much simpler to include these helpers.
  67. ///
  68. /// The validator returns true if the rule should still be considered valid.
  69. /// </summary>
  70. public void AddValidator(Function<bool> validator) {
  71. if (_validators == null) _validators = new List<Function<bool>>();
  72. _validators.Add(validator);
  73. }
  74. /// <summary>
  75. /// Gets the logical parameters to the dynamic site in the form of Expressions.
  76. /// </summary>
  77. public IList<Expression> Parameters {
  78. get {
  79. return _parameters;
  80. }
  81. }
  82. /// <summary>
  83. /// Allocates a temporary variable for use during the rule.
  84. /// </summary>
  85. public Variable GetTemporary(Type type, string name) {
  86. if (_temps == null) {
  87. _temps = new List<Variable>();
  88. }
  89. Variable ret = Variable.Temporary(SymbolTable.StringToId(name), null, type);
  90. _temps.Add(ret);
  91. return ret;
  92. }
  93. public Expression MakeReturn(ActionBinder binder, Expression expr) {
  94. // we create a temporary here so that ConvertExpression doesn't need to (because it has no way to declare locals).
  95. if (expr.Type != typeof(void)) {
  96. Variable variable = GetTemporary(expr.Type, "$retVal");
  97. Expression read = Ast.ReadDefined(variable);
  98. Expression conv = binder.ConvertExpression(read, ReturnType);
  99. if (conv == read) return Ast.Return(expr);
  100. return Ast.Return(Ast.Comma(Ast.Assign(variable, expr), conv));
  101. }
  102. return Ast.Return(binder.ConvertExpression(expr, ReturnType));
  103. }
  104. public Expression MakeError(Expression expr) {
  105. if (expr != null) {
  106. // TODO: Change to ConvertHelper
  107. if (!TypeUtils.CanAssign(typeof(Exception), expr.Type)) {
  108. expr = Ast.Convert(expr, typeof(Exception));
  109. }
  110. }
  111. _error = true;
  112. return Ast.Throw(expr);
  113. }
  114. public bool IsError {
  115. get {
  116. return _error;
  117. }
  118. internal set {
  119. _error = value;
  120. }
  121. }
  122. public void AddTest(Expression expression) {
  123. Assert.NotNull(expression);
  124. if (_test == null) {
  125. _test = expression;
  126. } else {
  127. _test = Ast.AndAlso(_test, expression);
  128. }
  129. }
  130. /// <summary>
  131. /// If not valid, this indicates that the given Test can never return true and therefore
  132. /// this rule should be removed from any RuleSets when convenient in order to
  133. /// reduce memory usage and the number of active rules.
  134. /// </summary>
  135. public bool IsValid {
  136. get {
  137. if (_validators == null) return true;
  138. foreach (Function<bool> v in _validators) {
  139. if (!v()) return false;
  140. }
  141. return true;
  142. }
  143. }
  144. public abstract Type ReturnType {
  145. get;
  146. }
  147. public void MakeTest(params Type[] types) {
  148. _test = MakeTestForTypes(types, 0);
  149. }
  150. /// <summary>
  151. /// Gets the logical parameters to the dynamic site in the form of Variables.
  152. /// </summary>
  153. internal Variable[] ParamVariables {
  154. get {
  155. return _paramVariables;
  156. }
  157. }
  158. /// <summary>
  159. /// The ActionBinder might prefer to interpret the target in some case. This property controls whether
  160. /// interpreting the target is OK, or if it needs to be compiled.
  161. ///
  162. /// If it needs to be compiled, the compiled target will be executed in an empty context, and so it will
  163. /// not be able to view any variables set by the test. The caller who sets CanInterpretTarget=false is
  164. /// responsible for ensuring that the test does not set any variables that the target depends on.
  165. ///
  166. /// The test should be such that it does not become invalid immediately. Otherwise, ActionBinder.UpdateSiteAndExecute
  167. /// can potentially loop infinitely.
  168. ///
  169. /// This should go away once the interpreter can support all the features required by the generated
  170. /// target statements
  171. /// </summary>
  172. public bool CanInterpretTarget {
  173. get { return _canInterpretTarget; }
  174. set {
  175. // InterpretedMode has not yet been updated to deal with CanInterpretTarget=false
  176. // Debug.Assert(value = true || !EngineOptions.InterpretedMode);
  177. _canInterpretTarget = value;
  178. }
  179. }
  180. /// <summary>
  181. /// Gets the temporary variables allocated by this rule.
  182. /// </summary>
  183. internal Variable[] TemporaryVariables {
  184. get {
  185. return _temps == null ? new Variable[] { } : _temps.ToArray();
  186. }
  187. }
  188. public static Expression MakeTypeTestExpression(Type t, Expression expr) {
  189. // we must always check for non-sealed types explicitly - otherwise we end up
  190. // doing fast-path behavior on a subtype which overrides behavior that wasn't
  191. // present for the base type.
  192. //TODO there's a question about nulls here
  193. if (CompilerHelpers.IsSealed(t) && t == expr.Type) {
  194. if (t.IsValueType) {
  195. return Ast.True();
  196. }
  197. return Ast.NotEqual(expr, Ast.Null());
  198. }
  199. return Ast.AndAlso(
  200. Ast.NotEqual(
  201. expr,
  202. Ast.Null()),
  203. Ast.Equal(
  204. Ast.Call(
  205. Ast.ConvertHelper(expr, typeof(object)),
  206. typeof(object).GetMethod("GetType")
  207. ),
  208. Ast.Constant(t)
  209. )
  210. );
  211. }
  212. /// <summary>
  213. /// Adds a templated constant that can enable code sharing across rules.
  214. /// </summary>
  215. public Expression AddTemplatedConstant(Type type, object value) {
  216. Contract.RequiresNotNull(type, "type");
  217. if (value != null) {
  218. if (!type.IsAssignableFrom(value.GetType())) {
  219. throw new ArgumentException("type must be assignable from value");
  220. }
  221. } else {
  222. if (!type.IsValueType) {
  223. throw new ArgumentException("value must not be null for value types");
  224. }
  225. }
  226. if (_templateData == null) _templateData = new List<object>(1);
  227. Type genType = typeof(TemplatedValue<>).MakeGenericType(type);
  228. object template = Activator.CreateInstance(genType, value, _templateData.Count);
  229. _templateData.Add(value);
  230. return Ast.ReadProperty(Ast.RuntimeConstant(template), genType.GetProperty("Value"));
  231. }
  232. public Expression AddTemplatedWeakConstant(Type type, object value) {
  233. if (value != null) {
  234. if (!type.IsAssignableFrom(value.GetType())) {
  235. throw new ArgumentException("type must be assignable from value");
  236. }
  237. } else {
  238. if (!type.IsValueType) {
  239. throw new ArgumentException("value must not be null for value types");
  240. }
  241. }
  242. Expression expr = AddTemplatedConstant(typeof(WeakReference), new WeakReference(value));
  243. return Ast.ConvertHelper(
  244. Ast.ReadProperty(expr, typeof(WeakReference).GetProperty("Target")),
  245. type
  246. );
  247. }
  248. internal int TemplateParameterCount {
  249. get {
  250. if (_templateData == null) return 0;
  251. return _templateData.Count;
  252. }
  253. }
  254. internal object[] TemplateData {
  255. get {
  256. return _templateData.ToArray();
  257. }
  258. }
  259. internal void RewriteTest(Expression test) {
  260. Debug.Assert(test != null && (object)test != (object)_test);
  261. _test = test;
  262. }
  263. internal void RewriteTarget(Expression target) {
  264. Debug.Assert(target != null && (object)target != (object)_test);
  265. _target = target;
  266. }
  267. public Expression MakeTestForTypes(Type[] types, int index) {
  268. Expression test = MakeTypeTest(types[index], index);
  269. if (index < types.Length - 1) {
  270. Expression nextTests = MakeTestForTypes(types, index + 1);
  271. if (ConstantCheck.IsConstant(test, true)) {
  272. return nextTests;
  273. } else if (ConstantCheck.IsConstant(nextTests, true)) {
  274. return test;
  275. } else {
  276. return Ast.AndAlso(test, nextTests);
  277. }
  278. } else {
  279. return test;
  280. }
  281. }
  282. public Expression MakeTypeTest(Type type, int index) {
  283. return MakeTypeTest(type, Parameters[index]);
  284. }
  285. public Expression MakeTypeTest(Type type, Expression tested) {
  286. if (type == null || type == typeof(None)) {
  287. return Ast.Equal(tested, Ast.Null());
  288. }
  289. return MakeTypeTestExpression(type, tested);
  290. }
  291. /// <summary>
  292. /// Gets the number of logical parameters the dynamic site is provided with.
  293. /// </summary>
  294. public int ParameterCount {
  295. get {
  296. return _parameters.Length;
  297. }
  298. }
  299. public Expression MakeTypeTestExpression(Type t, int param) {
  300. return MakeTypeTestExpression(t, Parameters[param]);
  301. }
  302. #if DEBUG
  303. public string Dump {
  304. get {
  305. using (System.IO.StringWriter writer = new System.IO.StringWriter()) {
  306. AstWriter.Dump(Test, "Test", writer);
  307. writer.WriteLine();
  308. AstWriter.Dump(Target, "Target", writer);
  309. return writer.ToString();
  310. }
  311. }
  312. }
  313. #endif
  314. }
  315. /// <summary>
  316. /// A rule is the mechanism that LanguageBinders use to specify both what code to execute (the Target)
  317. /// for a particular action on a particular set of objects, but also a Test that guards the Target.
  318. /// Whenver the Test returns true, it is assumed that the Target will be the correct action to
  319. /// take on the arguments.
  320. ///
  321. /// In the current design, a StandardRule is also used to provide a mini binding scope for the
  322. /// parameters and temporary variables that might be needed by the Test and Target. This will
  323. /// probably change in the future as we unify around the notion of CodeBlocks.
  324. /// </summary>
  325. /// <typeparam name="T">The type of delegate for the DynamicSites this rule may apply to.</typeparam>
  326. public class StandardRule<T> : StandardRule {
  327. private SmallRuleSet<T> _monomorphicRuleSet;
  328. public StandardRule() {
  329. int firstParameter = DynamicSiteHelpers.IsFastTarget(typeof(T)) ? 1 : 2;
  330. ParameterInfo[] pis = typeof(T).GetMethod("Invoke").GetParameters();
  331. if (!DynamicSiteHelpers.IsBigTarget(typeof(T))) {
  332. _parameters = new Expression[pis.Length - firstParameter];
  333. List<Variable> paramVars = new List<Variable>();
  334. for (int i = firstParameter; i < pis.Length; i++) {
  335. Variable p = MakeParameter(i, "$arg" + (i - firstParameter), pis[i].ParameterType);
  336. paramVars.Add(p);
  337. _parameters[i - firstParameter] = Ast.ReadDefined(p);
  338. }
  339. _paramVariables = paramVars.ToArray();
  340. } else {
  341. MakeTupleParameters(firstParameter, typeof(T).GetGenericArguments()[0]);
  342. }
  343. }
  344. private void MakeTupleParameters(int firstParameter, Type tupleType) {
  345. int count = Tuple.GetSize(tupleType);
  346. Variable tupleVar = MakeParameter(firstParameter, "$arg0", tupleType);
  347. _paramVariables = new Variable[] { tupleVar };
  348. Expression tuple = Ast.ReadDefined(tupleVar);
  349. _parameters = new Expression[count];
  350. for (int i = 0; i < _parameters.Length; i++) {
  351. Expression tupleAccess = tuple;
  352. foreach (PropertyInfo pi in Tuple.GetAccessPath(tupleType, i)) {
  353. tupleAccess = Ast.ReadProperty(tupleAccess, pi);
  354. }
  355. _parameters[i] = tupleAccess;
  356. }
  357. }
  358. private Variable MakeParameter(int index, string name, Type type) {
  359. Variable ret = Variable.Parameter(null, SymbolTable.StringToId(name), type);
  360. ret.ParameterIndex = index;
  361. return ret;
  362. }
  363. /// <summary>
  364. /// Each rule holds onto an immutable RuleSet that contains this rule only.
  365. /// This should heavily optimize monomorphic call sites.
  366. /// </summary>
  367. internal SmallRuleSet<T> MonomorphicRuleSet {
  368. get {
  369. if (_monomorphicRuleSet == null) {
  370. _monomorphicRuleSet = new SmallRuleSet<T>(new StandardRule<T>[] { this });
  371. }
  372. return _monomorphicRuleSet;
  373. }
  374. }
  375. /// <summary>
  376. /// Execute the target of the rule (in either interpreted or compiled mode)
  377. /// </summary>
  378. internal object ExecuteTarget(object site, CodeContext context, object [] args) {
  379. if (CanInterpretTarget) {
  380. // Interpret the target in the common case
  381. return Interpreter.Execute(context, Target);
  382. }
  383. // The target cannot be interpreted. We will execute the compiled rule. However, this will
  384. // include the test as well. The caller who sets CanInterpretTarget=false is responsible
  385. // for ensuring that the test is (mostly) idempotent.
  386. // Caller should have packaged up arguments for BigTarget
  387. Debug.Assert(!DynamicSiteHelpers.IsBigTarget(typeof(T)) || (args[0] is Tuple));
  388. T targetDelegate = MonomorphicRuleSet.GetOrMakeTarget(context);
  389. object[] prefixArgs;
  390. if (DynamicSiteHelpers.IsFastTarget(typeof(T))) {
  391. prefixArgs = new object[] { site };
  392. } else {
  393. prefixArgs = new object[] { site, context };
  394. }
  395. args = ArrayUtils.AppendRange(prefixArgs, args);
  396. try {
  397. return typeof(T).GetMethod("Invoke").Invoke(targetDelegate, args);
  398. } catch (TargetInvocationException e) {
  399. // Unwrap the real (inner) exception and raise it
  400. throw ExceptionHelpers.UpdateForRethrow(e.InnerException);
  401. }
  402. }
  403. /// <summary>
  404. /// Emits the test and target of the rule emitting the code using the provided
  405. /// compiler, branching to ifFalse if the test is not satisfied.
  406. /// </summary>
  407. internal void Emit(Compiler cg, Label ifFalse) {
  408. Assert.NotNull(_test, _target);
  409. // Need to make sure we aren't generating into two different CodeGens at the same time
  410. lock (this) {
  411. // First, finish binding my variable references
  412. // And rewrite the AST if needed
  413. if (_references == null) {
  414. AstRewriter.RewriteRule(this);
  415. _references = RuleBinder.Bind(_test, _target, ReturnType);
  416. }
  417. cg.References = _references;
  418. foreach (VariableReference vr in _references.Values) {
  419. vr.CreateSlot(cg);
  420. }
  421. if (_test != null) {
  422. cg.EmitBranchFalse(_test, ifFalse);
  423. }
  424. // Now do the generation
  425. cg.EmitExpression(_target);
  426. // free any temps now that we're done generating
  427. // TODO: Keep temp slots aside sot that they can be freed
  428. //if (_temps != null) {
  429. // foreach (Variable vr in _temps) {
  430. // cg.FreeLocalTmp(vr.Slot);
  431. // }
  432. //}
  433. }
  434. }
  435. [Confined]
  436. public override string/*!*/ ToString() {
  437. return string.Format("StandardRule({0})", _target);
  438. }
  439. public override Type ReturnType {
  440. get {
  441. return typeof(T).GetMethod("Invoke").ReturnType;
  442. }
  443. }
  444. #region Factory Methods
  445. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] // TODO: fix
  446. public static StandardRule<T> Simple(ActionBinder binder, MethodBase target, params Type[] types) {
  447. StandardRule<T> ret = new StandardRule<T>();
  448. BindingTarget bindingTarget = MethodBinder.MakeBinder(binder, target.Name, new MethodBase[] { target }).MakeBindingTarget(CallType.None, types);
  449. ret.MakeTest(types);
  450. ret.Target = ret.MakeReturn(binder, bindingTarget.MakeExpression(ret, ret.Parameters));
  451. return ret;
  452. }
  453. #endregion
  454. /// <summary>
  455. /// Returns a TemplatedRuleBuilder which can be used to replace data. See TemplatedRuleBuilder
  456. /// for more information.
  457. /// </summary>
  458. /// <returns></returns>
  459. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
  460. public TemplatedRuleBuilder<T> GetTemplateBuilder() {
  461. if (_test == null || _target == null) throw new InvalidOperationException();
  462. if (_templateData == null) throw new InvalidOperationException("no template arguments created");
  463. return new TemplatedRuleBuilder<T>(this);
  464. }
  465. }
  466. }