public static UnaryExpression GetParamsList(StandardRule <T> rule) { return(Ast.Convert( rule.Parameters[rule.ParameterCount - 1], typeof(IList <object>) )); }
public override RuleSet <T> AddRule(StandardRule <T> newRule) { // Can the rule become invalidated between its creation and // its insertion into the set? Debug.Assert(newRule.IsValid, "Adding an invalid rule"); IList <StandardRule <T> > newRules = new List <StandardRule <T> >(); newRules.Add(newRule); foreach (StandardRule <T> rule in _rules) { if (rule.IsValid) { newRules.Add(rule); } } if (newRules.Count > MaxRules) { return(EmptyRuleSet <T> .FixedInstance); } else { return(new SmallRuleSet <T>(newRules)); } }
public StandardRule <T> GetRule <T>(CodeContext callerContext, DynamicAction action, object[] args) { Contract.RequiresNotNull(action, "action"); //Debug.Assert(action.Kind != ActionKind.GetMember || ((GetMemberAction)action).Name != SymbolTable.StringToId("x")); StandardRule <T> rule = _ruleCache.FindRule <T>(callerContext, action, args); if (rule != null) { return(rule); } NoteRuleCreation(action, args); 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 return(rule); }
/// <summary> /// Internal helper to produce the actual expression used for the error when emitting /// the error into a rule. /// </summary> public Statement MakeErrorForRule(StandardRule rule, ActionBinder binder) { if (_value != null) { rule.IsError = true; return rule.MakeReturn(binder, _value); } return rule.MakeError(_exception); }
/// <summary> /// Provides a way for the binder to provide a custom error message when lookup fails. Just /// doing this for the time being until we get a more robust error return mechanism. /// </summary> public virtual Statement MakeReadOnlyMemberError <T>(StandardRule <T> rule, Type type, string name) { return(rule.MakeError( Ast.Ast.New( typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }), Ast.Ast.Constant(name) ) )); }
/// <summary> /// Internal helper to produce the actual expression used for the error when emitting /// the error into a rule. /// </summary> public Statement MakeErrorForRule(StandardRule rule, ActionBinder binder) { if (_value != null) { rule.IsError = true; return(rule.MakeReturn(binder, _value)); } return(rule.MakeError(_exception)); }
public override RuleSet <T> AddRule(StandardRule <T> newRule) { if (_supportAdding) { return(newRule.MonomorphicRuleSet); } else { return(this); } }
/// <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(); } } }
/// <summary> /// Updates the provided rule to use the previously created templated rule with the newly provided parameters. /// </summary> public void CopyTemplateToRule(CodeContext context, StandardRule <T> rule) { if (_rule.TemplateParameterCount != rule.TemplateParameterCount) { throw new ArgumentException(String.Format("Incompatible rules. Expected {0} template parameters, got {1}", _rule.TemplateParameterCount, rule.TemplateParameterCount)); } Delegate existingDelegate = (Delegate)(object)_rule.MonomorphicRuleSet.GetOrMakeTarget(context); rule.MonomorphicRuleSet.RawTarget = CloneDelegate(rule.TemplateData, existingDelegate); }
public static Expression MakeParamsTest(StandardRule <T> rule, object paramArg, Expression listArg) { return(Ast.AndAlso( Ast.TypeIs(listArg, typeof(ICollection <object>)), Ast.Equal( Ast.ReadProperty( Ast.Convert(listArg, typeof(ICollection <object>)), typeof(ICollection <object>).GetProperty("Count") ), rule.AddTemplatedConstant(typeof(int), ((IList <object>)paramArg).Count) ) )); }
public Tret UpdateBindingAndInvoke(CodeContext context, T0 arg0) { StandardRule <BigDynamicSiteTarget <T0, Tret> > rule = _rules.GetRule(context, arg0); if (rule != null) { // site is truly polymorphic, build the polymorphic method _target = _rules.GetOrMakeTarget(context); return(_target(this, context, arg0)); } return((Tret)context.LanguageContext.Binder.UpdateSiteAndExecute <BigDynamicSiteTarget <T0, Tret> >(context, Action, Tuple.GetTupleValues(arg0), this, ref _target, ref _rules)); }
public MethodBinderContext(ActionBinder actionBinder #if FULL , StandardRule rule #endif ) { _actionBinder = actionBinder; #if FULL _rule = rule; #endif }
public Tret UpdateBindingAndInvoke(T0 arg0) { StandardRule <FastDynamicSiteTarget <T0, Tret> > rule = _rules.GetRule(Context, arg0); if (rule != null) { // site is truly polymorphic, build the polymorphic method _target = _rules.GetOrMakeTarget(Context); return(_target(this, arg0)); } return((Tret)Context.LanguageContext.Binder.UpdateSiteAndExecute <FastDynamicSiteTarget <T0, Tret> >(Context, Action, new object[] { arg0 }, this, ref _target, ref _rules)); }
/// <summary> /// Sees if the target is implemented with ActionOnCallAttribute and if so attempts to get a rule from the attribute. /// </summary> /// <returns>True if the method implements ActionOnCall, false if not.</returns> private bool MakeActionOnCallRule(MethodBase target) { // see if the method provides a custom inline action object[] attrs = target.GetCustomAttributes(typeof(ActionOnCallAttribute), false); if (attrs.Length > 0) { StandardRule <T> rule = ((ActionOnCallAttribute)attrs[0]).GetRule <T>(Context, _args); if (rule != null) { _rule = rule; return(true); } } return(false); }
public static Expression MakeNecessaryTests(StandardRule <T> rule, IList <Type[]> necessaryTests, Expression [] arguments) { Expression typeTest = Ast.Constant(true); if (necessaryTests.Count > 0) { Type[] testTypes = null; for (int i = 0; i < necessaryTests.Count; i++) { if (necessaryTests[i] == null) { continue; } if (testTypes == null) { testTypes = new Type[necessaryTests[i].Length]; } for (int j = 0; j < necessaryTests[i].Length; j++) { if (testTypes[j] == null || testTypes[j].IsAssignableFrom(necessaryTests[i][j])) { // no test yet or more specific test testTypes[j] = necessaryTests[i][j]; } } } if (testTypes != null) { for (int i = 0; i < testTypes.Length; i++) { if (testTypes[i] != null) { Debug.Assert(i < arguments.Length); typeTest = Ast.AndAlso(typeTest, rule.MakeTypeTest(testTypes[i], arguments[i])); } } } } return(typeTest); }
public MemberBinderHelper(CodeContext context, TActionKind action, object [] args) : base(context, action) { Contract.RequiresNotNull(args, "args"); if (args.Length == 0) { throw new ArgumentException("args must have at least one member"); } _args = args; _target = args[0]; if (CompilerHelpers.IsStrongBox(_target)) { _strongBoxType = _target.GetType(); _target = ((IStrongBox)_target).Value; } _rule = new StandardRule <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); }
public virtual Statement MakeInvalidParametersError(MethodBinder binder, DynamicAction action, CallType callType, IList <MethodBase> targets, StandardRule rule, object [] args) { int minArgs = Int32.MaxValue; int maxArgs = Int32.MinValue; int maxDflt = Int32.MinValue; int argsProvided = args.Length - 1; // -1 to remove the object we're calling bool hasArgList = false, hasNamedArgument = false; Dictionary <string, bool> namedArgs = new Dictionary <string, bool>(); CallAction ca = action as CallAction; if (ca != null) { hasNamedArgument = ca.Signature.HasNamedArgument(); int dictArgIndex = ca.Signature.IndexOf(ArgumentKind.Dictionary); if (dictArgIndex > -1) { argsProvided--; IAttributesCollection iac = args[dictArgIndex + 1] as IAttributesCollection; if (iac != null) { foreach (KeyValuePair <object, object> kvp in iac) { namedArgs[(string)kvp.Key] = false; } } } argsProvided += GetParamsArgumentCountAdjust(ca, args); foreach (SymbolId si in ca.Signature.GetArgumentNames()) { namedArgs[SymbolTable.IdToString(si)] = false; } } else { maxArgs = minArgs = rule.Parameters.Length; maxDflt = 0; } foreach (MethodBase mb in targets) { if (callType == CallType.ImplicitInstance && CompilerHelpers.IsStatic(mb)) { continue; } ParameterInfo[] pis = mb.GetParameters(); int cnt = pis.Length; int dflt = 0; if (!CompilerHelpers.IsStatic(mb) && callType == CallType.None) { cnt++; } foreach (ParameterInfo pi in pis) { if (pi.ParameterType == typeof(CodeContext)) { cnt--; } else if (CompilerHelpers.IsParamArray(pi)) { cnt--; hasArgList = true; } else if (CompilerHelpers.IsParamDictionary(pi)) { cnt--; } else if (!CompilerHelpers.IsMandatoryParameter(pi)) { dflt++; cnt--; } namedArgs[pi.Name] = true; } minArgs = System.Math.Min(cnt, minArgs); maxArgs = System.Math.Max(cnt, maxArgs); maxDflt = System.Math.Max(dflt, maxDflt); } foreach (KeyValuePair <string, bool> kvp in namedArgs) { if (kvp.Value == false) { // unbound named argument. return(rule.MakeError( Ast.Ast.Call( typeof(RuntimeHelpers).GetMethod("TypeErrorForExtraKeywordArgument"), Ast.Ast.Constant(binder._name, typeof(string)), Ast.Ast.Constant(kvp.Key, typeof(string)) ) )); } } return(rule.MakeError( Ast.Ast.Call( typeof(RuntimeHelpers).GetMethod("TypeErrorForIncorrectArgumentCount", new Type[] { typeof(string), typeof(int), typeof(int), typeof(int), typeof(int), typeof(bool), typeof(bool) }), Ast.Ast.Constant(binder._name, typeof(string)), // name Ast.Ast.Constant(minArgs), // min formal normal arg cnt Ast.Ast.Constant(maxArgs), // max formal normal arg cnt Ast.Ast.Constant(maxDflt), // default cnt Ast.Ast.Constant(argsProvided), // args provided Ast.Ast.Constant(hasArgList), // hasArgList Ast.Ast.Constant(hasNamedArgument) // kwargs provided ) )); }
public abstract RuleSet <T> AddRule(StandardRule <T> newRule);
public void AddRule <T>(object[] args, StandardRule <T> newRule) { GetOrMakeTree <T>().AddRule(args, newRule); }
protected Expression[] GetICallableParameters(Type t, StandardRule <T> rule) { List <Expression> plainArgs = new List <Expression>(); List <KeyValuePair <SymbolId, Expression> > named = new List <KeyValuePair <SymbolId, Expression> >(); Expression splat = null, kwSplat = null; Expression instance = null; for (int i = 1; i < rule.Parameters.Length; i++) { switch (Action.Signature.GetArgumentKind(i - 1)) { case ArgumentKind.Simple: plainArgs.Add(rule.Parameters[i]); break; case ArgumentKind.List: splat = rule.Parameters[i]; break; case ArgumentKind.Dictionary: kwSplat = rule.Parameters[i]; break; case ArgumentKind.Named: named.Add(new KeyValuePair <SymbolId, Expression>(Action.Signature.GetArgumentName(i - 1), rule.Parameters[i])); break; case ArgumentKind.Instance: instance = rule.Parameters[i]; break; case ArgumentKind.Block: default: throw new NotImplementedException(); } } Expression argsArray = Ast.NewArrayHelper(typeof(object[]), plainArgs.ToArray()); if (splat != null) { argsArray = Ast.Call( typeof(BinderOps).GetMethod("GetCombinedParameters"), argsArray, Ast.ConvertHelper(splat, typeof(object)) ); } if (kwSplat != null || named.Count > 0) { // IFancyCallable.Call(context, args, names) Debug.Assert(instance == null); // not supported, no IFancyCallableWithInstance Expression names; if (named.Count > 0) { List <Expression> constNames = new List <Expression>(); List <Expression> namedValues = new List <Expression>(); foreach (KeyValuePair <SymbolId, Expression> kvp in named) { constNames.Add(Ast.Constant(SymbolTable.IdToString(kvp.Key))); namedValues.Add(kvp.Value); } argsArray = Ast.Call( typeof(BinderOps).GetMethod("GetCombinedParameters"), argsArray, Ast.NewArrayHelper(typeof(object[]), namedValues.ToArray()) ); names = Ast.NewArrayHelper(typeof(string[]), constNames.ToArray()); } else { names = Ast.Null(typeof(string[])); } if (kwSplat != null) { Variable namesVar = rule.GetTemporary(typeof(string[]), "names"); argsArray = Ast.Comma( Ast.Assign(namesVar, names), Ast.Call( typeof(BinderOps).GetMethod("GetCombinedKeywordParameters"), argsArray, Ast.ConvertHelper(kwSplat, typeof(IAttributesCollection)), Ast.Read(namesVar) ) ); return(new Expression[] { Ast.CodeContext(), argsArray, Ast.Read(namesVar) }); } return(new Expression[] { Ast.CodeContext(), argsArray, names }); } // ICallable.Call(context, args) if (instance != null && typeof(ICallableWithThis).IsAssignableFrom(t)) { return(new Expression[] { Ast.CodeContext(), instance, argsArray }); } return(new Expression[] { Ast.CodeContext(), argsArray }); }
/// <summary> /// Creates a call to this MethodTarget with the specified parameters. Casts are inserted to force /// the types to the provided known types. /// </summary> /// <param name="binder"></param> /// <param name="rule"></param> /// <param name="parameters"></param> /// <param name="knownTypes"></param> /// <returns></returns> public Expression MakeExpression(ActionBinder binder, StandardRule rule, Expression[] parameters, Type[] knownTypes) { Expression[] args = parameters; if (knownTypes != null) { args = new Expression[parameters.Length]; for (int i = 0; i < args.Length; i++) { args[i] = parameters[i]; if (knownTypes[i] != null && !knownTypes[i].IsAssignableFrom(parameters[i].Type)) { args[i] = Ast.Convert(parameters[i], CompilerHelpers.GetVisibleType(knownTypes[i])); } } } return MakeExpression(binder, rule, args); }
public Expression MakeExpression(ActionBinder binder, StandardRule rule, Expression[] parameters) { MethodBinderContext context = new MethodBinderContext(binder, rule); Expression check = Ast.True(); if (_binder.IsBinaryOperator) { // TODO: only if we have a narrowing level // need to emit check to see if args are convertible... for (int i = 0; i < _argBuilders.Count; i++) { Expression checkedExpr = _argBuilders[i].CheckExpression(context, parameters); if(checkedExpr != null) { check = Ast.AndAlso(check, checkedExpr); } } } Expression[] args = new Expression[_argBuilders.Count]; for (int i = 0; i < _argBuilders.Count; i++) { args[i] = _argBuilders[i].ToExpression(context, parameters); } MethodInfo mi = Method as MethodInfo; Expression ret, call; if (!Method.IsPublic || !Method.DeclaringType.IsVisible) { if (mi != null) { mi = CompilerHelpers.GetCallableMethod(mi); } } if (Method.IsPublic && Method.DeclaringType.IsVisible) { // public method if (mi != null) { Expression instance = mi.IsStatic ? null : _instanceBuilder.ToExpression(context, parameters); call = Ast.SimpleCallHelper(instance, mi, args); } else { call = Ast.SimpleNewHelper((ConstructorInfo)Method, args); } } else { // Private binding, invoke via reflection if (mi != null) { Expression instance = mi.IsStatic ? null : _instanceBuilder.ToExpression(context, parameters); call = Ast.Call( Ast.RuntimeConstant(mi), typeof(MethodInfo).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) }), Ast.ConvertHelper(instance, typeof(object)), Ast.NewArrayHelper(typeof(object[]), args) ); } else { call = Ast.Call( Ast.RuntimeConstant((ConstructorInfo)Method), typeof(ConstructorInfo).GetMethod("Invoke", new Type[] { typeof(object[]) }), Ast.NewArrayHelper(typeof(object[]), args) ); } } ret = _returnBuilder.ToExpression(context, _argBuilders, parameters, call); List<Expression> updates = null; for (int i = 0; i < _argBuilders.Count; i++) { Expression next = _argBuilders[i].UpdateFromReturn(context, parameters); if (next != null) { if (updates == null) updates = new List<Expression>(); updates.Add(next); } } if (updates != null) { updates.Insert(0, ret); ret = Ast.Comma(0, updates.ToArray()); } if (!check.IsConstant(true)) { ret = Ast.Condition( check, Ast.ConvertHelper(ret, typeof(object)), GetNotImplemented() ); } return ret; }
public void AddRule(object[] args, StandardRule <T> rule) { GetRuleList(args).AddLast(rule); }
/// <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); }
internal void AddRule <T>(DynamicAction action, object[] args, StandardRule <T> rule) { ActionRuleCache actionRuleCache = FindActionRuleCache(action); actionRuleCache.AddRule <T>(args, rule); }
/// <summary> /// Provides a way for the binder to provide a custom error message when lookup fails. Just /// doing this for the time being until we get a more robust error return mechanism. /// </summary> public virtual Statement MakeUndeletableMemberError <T>(StandardRule <T> rule, Type type, string name) { return(MakeReadOnlyMemberError <T>(rule, type, name)); }
internal static void UpdateSite <T>(CodeContext callerContext, object site, ref T target, ref RuleSet <T> rules, StandardRule <T> rule) { lock (site) { bool monomorphic = rules.HasMonomorphicTarget(target); rules = rules.AddRule(rule); if (monomorphic || rules == EmptyRuleSet <T> .FixedInstance) { target = rule.MonomorphicRuleSet.GetOrMakeTarget(callerContext); } else { target = rules.GetOrMakeTarget(callerContext); } } }
internal TemplatedRuleBuilder(StandardRule <T> template) { _rule = template; }