internal static CodeContext GetEvaluationContext <T>(CodeContext context, ref object [] args) { context = new CodeContext(new Scope(context.Scope, null), context.LanguageContext, context.ModuleContext); if (DynamicSiteHelpers.IsBigTarget(typeof(T))) { args = new object[] { Tuple.MakeTuple(typeof(T).GetGenericArguments()[0], args) }; } return(context); }
/// <summary> /// Gets a rule, updates the site that called, and then returns the result of executing the rule. /// </summary> /// <typeparam name="T">The type of the DynamicSite the rule is being produced for.</typeparam> /// <param name="action">The Action the rule is being produced for.</param> /// <param name="args">The arguments to the rule as provided from the call site at runtime.</param> /// <param name="callerContext">The CodeContext that is requesting the rule and that should be used for conversions.</param> /// <param name="rules"></param> /// <param name="site"></param> /// <param name="target"></param> /// <returns>The result of executing the rule.</returns> internal object UpdateSiteAndExecute <T>(CodeContext callerContext, DynamicAction action, object[] args, object site, ref T target, ref RuleSet <T> rules) { Contract.RequiresNotNull(action, "action"); //Debug.Assert(action.Kind != ActionKind.GetMember || ((GetMemberAction)action).Name != SymbolTable.StringToId("x")); object result; StandardRule <T> rule = _ruleCache.ExecuteRuleAndUpdateSite <T>(callerContext, action, args, site, ref target, ref rules, out result); if (rule != null) { return(result); } NoteRuleCreation(action, args); for (; ;) { IDynamicObject ndo = args[0] as IDynamicObject; if (ndo != null) { rule = ndo.GetRule <T>(action, callerContext, args); Debug.Assert(rule == null || rule.Target != null && rule.Test != null); } rule = rule ?? MakeRule <T>(callerContext, action, args); Debug.Assert(rule != null && rule.Target != null && rule.Test != null); #if DEBUG AstWriter.Dump(rule); #endif object[] callArgs = args; if (args.Length > 6) { // BigDynamicSite callArgs = new object[] { Tuple.MakeTuple(rule.ParamVariables[0].Type, args) }; } CodeContext tmpCtx = callerContext.Scope.GetTemporaryVariableContext(callerContext, rule.ParamVariables, callArgs); try { if ((bool)rule.Test.Evaluate(tmpCtx)) { if (site != null) { DynamicSiteHelpers.UpdateSite <T>(callerContext, site, ref target, ref rules, rule); } _ruleCache.AddRule(action, args, rule); return(rule.Target.Execute(tmpCtx)); } } finally { tmpCtx.Scope.TemporaryStorage.Clear(); } } }
protected override T MakeTarget(CodeContext context) { if (_rules.Count == 1 && this != _rules[0].MonomorphicRuleSet) { // use the monomorphic rule if we only have 1 rule. return(_rules[0].MonomorphicRuleSet.GetOrMakeTarget(context)); } PerfTrack.NoteEvent(PerfTrack.Categories.Rules, "GenerateRule"); MethodInfo mi = typeof(T).GetMethod("Invoke"); CodeGen cg = ScriptDomainManager.CurrentManager.Snippets.Assembly.DefineMethod( StubName, mi.ReturnType, ReflectionUtils.GetParameterTypes(mi.GetParameters()), new ConstantPool() ); cg.EmitLineInfo = false; cg.Binder = context.LanguageContext.Binder; if (DynamicSiteHelpers.IsFastTarget(typeof(T))) { cg.ContextSlot = new PropertySlot(cg.ArgumentSlots[0], typeof(FastDynamicSite).GetProperty("Context")); } else { cg.ContextSlot = cg.ArgumentSlots[1]; } foreach (StandardRule <T> rule in _rules) { Label nextTest = cg.DefineLabel(); rule.Emit(cg, nextTest); cg.MarkLabel(nextTest); } EmitNoMatch(cg); if (_rules.Count == 1 && this == _rules[0].MonomorphicRuleSet && _rules[0].TemplateParameterCount > 0 && cg.IsDynamicMethod) { _monomorphicTemplate = (DynamicMethod)cg.MethodInfo; } return((T)(object)cg.CreateDelegate(typeof(T))); }
protected override T MakeTarget(CodeContext context) { if (DynamicSiteHelpers.IsBigTarget(typeof(T))) { if (DynamicSiteHelpers.IsFastTarget(typeof(T))) { return((T)(object)DynamicSiteHelpers.MakeUninitializedBigFastTarget(typeof(T))); } else { return((T)(object)DynamicSiteHelpers.MakeUninitializedBigTarget(typeof(T))); } } else if (DynamicSiteHelpers.IsFastTarget(typeof(T))) { return((T)(object)DynamicSiteHelpers.MakeUninitializedFastTarget(typeof(T))); } else { return((T)(object)DynamicSiteHelpers.MakeUninitializedTarget(typeof(T))); } }
public override StandardRule <T> GetRule(CodeContext context, params object[] args) { context = DynamicSiteHelpers.GetEvaluationContext <T>(context, ref args); for (int i = 0; i < _rules.Count; i++) { StandardRule <T> rule = _rules[i]; if (!rule.IsValid) { continue; } CodeContext tmpCtx = context.Scope.GetTemporaryVariableContext(context, rule.ParamVariables, args); try { if ((bool)rule.Test.Evaluate(tmpCtx)) { return(rule); } } finally { tmpCtx.Scope.TemporaryStorage.Clear(); } } return(null); }
/// <summary> /// Gets a rule for the provided action and arguments and executes it without compiling. /// </summary> public object Execute(CodeContext cc, DynamicAction action, object[] args) { return(DynamicSiteHelpers.Execute(cc, this, action, args)); }
/// <summary> /// This function is a little complex: /// /// 1. It needs to be thread-safe and atomic. This means the iteration over the list /// needs to happen w/o releasing a lock, and the tests also get run under lock /// (they’re simple operations and therefore are safe to do this). But the target /// is not safe to run under a lock because it may block and cause a deadlock. Therefore /// we need to release the lock before running the target. /// 2. The code for doing the evaluation is a little complex, and the threading issues makes /// it more complex. Therefore factoring it into a version which just looks for a rule, /// and a version which looks and executes a rule is either going to obscure the locking issues /// or result in a reasonable duplication of code which may be error prone to maintain. /// 3. The updates of the sites needs to happen before the execution of the rule. This /// is due to both stack overflow and performance issues. If a function goes recursive and we /// haven’t updated the recursive-call site before then we’ll repeatedly generate and evaluate /// rules until we stack overflow, and if we don’t stack overflow our perf will /// suck until the method unwinds once. /// /// For those reasons we get the one big method which takes 7 parameters to handle both updating /// and executing. One of those parameters is the bool flag to indicate that we should execute /// the rule (that’s how that decision is made). 3 more are the result of the execution and the /// target/rule list to update for the caller. One more is the site object which we need to lock /// on to make the update to the site atomic. And finally we get the CodeContext which now flows /// in instead of having RuleTree hold onto a LanguageContext. This is because we need /// the full CodeContext to execute the test/target and it needs to be the real CodeContext so /// we get the proper set of locals/globals flowing through. /// </summary> private StandardRule <T> GetRuleMaybeExecute(CodeContext callerContext, object[] args, bool execute, object site, ref T target, ref RuleSet <T> rules, out object result) { // TODO: We can do better granularity than just types. LinkedList <StandardRule <T> > ruleList = GetRuleList(args); if (DynamicSiteHelpers.IsBigTarget(typeof(T))) { args = new object[] { Tuple.MakeTuple(typeof(T).GetGenericArguments()[0], args) }; } bool lockReleased = false; int index = 0; Monitor.Enter(ruleList); try { LinkedListNode <StandardRule <T> > node = ruleList.First; while (node != null) { StandardRule <T> rule = node.Value; if (!rule.IsValid) { LinkedListNode <StandardRule <T> > nodeToRemove = node; node = node.Next; ruleList.Remove(nodeToRemove); continue; } PerfTrack.NoteEvent(PerfTrack.Categories.RuleEvaluation, "Evaluating " + index++ + " rule in tree"); CodeContext tmpCtx = callerContext.Scope.GetTemporaryVariableContext(callerContext, rule.ParamVariables, args); try { if ((bool)rule.Test.Evaluate(tmpCtx)) { // Tentative optimization of moving rule to front of list when found ruleList.Remove(node); ruleList.AddFirst(node); if (site != null) { DynamicSiteHelpers.UpdateSite <T>(callerContext, site, ref target, ref rules, rule); } // release the lock for calling the target which may block, we assume // the test performs no synchronization Monitor.Exit(ruleList); lockReleased = true; if (execute) { result = rule.Target.Execute(tmpCtx); } else { result = null; } return(rule); } } finally { tmpCtx.Scope.TemporaryStorage.Clear(); } node = node.Next; } } finally { if (!lockReleased) { Monitor.Exit(ruleList); } } PerfTrack.NoteEvent(PerfTrack.Categories.Rules, "NoMatch" + index); result = null; return(null); }