public DynamicMetaObject DoOperation(string operation, OverloadResolverFactory resolverFactory, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNull(operation, "operation"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); ContractUtils.RequiresNotNullItems(args, "args"); return MakeGeneralOperatorRule(operation, resolverFactory, args); // Then try comparison / other ExpressionType }
public override Ast ConvertExpression(Ast expr, Type toType, ConversionResultKind kind, OverloadResolverFactory resolverFactory) { Type exprType = expr.Type; Type visType = CompilerHelpers.GetVisibleType(toType); if (typeof(IFn).IsAssignableFrom(exprType) && typeof(Delegate).IsAssignableFrom(visType)) return Ast.Call(typeof(Converter).GetMethod("ConvertToDelegate"), Ast.Convert(expr, typeof(IFn)), Expression.Constant(visType)); // Follow through on our promise to convert IEnumerable<Object> or IEnumerable to IEnumerable<T> for any T if (toType.IsGenericType && ! toType.IsAssignableFrom(expr.Type)) { // The following is inspired by IronPython.Runtime.Binding.Python.ConversionBinder.FallbackConvert Type genTo = toType.GetGenericTypeDefinition(); if ( genTo == typeof(IList<>)) { return MakeToGenericConversion(expr,toType,typeof(IList<object>),typeof(ListGenericWrapper<>)); } else if (genTo == typeof(IDictionary<,>)) { return MakeToGenericConversion(expr,toType,typeof(IDictionary<object,object>),typeof(DictionaryGenericWrapper<,>)); } else if (genTo == typeof(IEnumerable<>)) { return MakeToGenericConversion(expr, toType, typeof(IEnumerable),typeof(IEnumerableOfTWrapper<>)); } } return base.ConvertExpression(expr, toType, kind, resolverFactory); }
protected override DynamicMetaObject SetBoundValue(OverloadResolverFactory factory, ActionBinder binder, Type type, DynamicMetaObject value, DynamicMetaObject instance, DynamicMetaObject errorSuggestion) { return new DynamicMetaObject( Expression.Condition( Ast.Call( typeof(PythonOps).GetMethod("SlotTrySetValue"), ((PythonOverloadResolverFactory)factory)._codeContext, AstUtils.Constant(GetSlot(), typeof(PythonTypeSlot)), AstUtils.Convert( instance.Expression, typeof(object) ), AstUtils.Constant(DynamicHelpers.GetPythonTypeFromType(type)), value.Expression ), AstUtils.Convert(value.Expression, typeof(object)), errorSuggestion != null ? errorSuggestion.Expression : Expression.Throw( Expression.Call( typeof(PythonOps).GetMethod("AttributeErrorForMissingAttribute", new Type[] { typeof(object), typeof(string) }), instance.Expression, Expression.Constant(Name) ), typeof(object) ) ), BindingRestrictions.Empty ); }
/// <summary> /// Creates the MetaObject for indexing directly into arrays or indexing into objects which have /// default members. Returns null if we're not an indexing operation. /// </summary> public DynamicMetaObject SetIndex(OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { if (args[0].LimitType.IsArray) { return MakeArrayIndexRule(resolverFactory, IndexType.Set, args); } return MakeMethodIndexRule(IndexType.Set, resolverFactory, args); }
public override Expression ConvertExpression(Expression expr, Type toType, ConversionResultKind kind, OverloadResolverFactory resolverFactory) { ContractUtils.RequiresNotNull(expr, "expr"); ContractUtils.RequiresNotNull(toType, "toType"); Type exprType = expr.Type; if (toType == typeof(object)) { if (exprType.IsValueType()) return Utils.Convert(expr, toType); else return expr; } if (toType.IsAssignableFrom(exprType)) return expr; Type visType = Context.Binder.PrivateBinding ? toType : CompilerHelpers.GetVisibleType(toType); return Binders.Convert( _context, visType, kind, expr ); }
/// <summary> /// Provides default binding for performing a call on the specified meta objects. /// </summary> /// <param name="signature">The signature describing the call</param> /// <param name="target">The meta object to be called.</param> /// <param name="args"> /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// </param> /// <param name="resolverFactory">Overload resolver factory.</param> /// <param name="errorSuggestion">The result should the object be uncallable.</param> /// <returns>A MetaObject representing the call or the error.</returns> public DynamicMetaObject Call(CallSignature signature, DynamicMetaObject errorSuggestion, OverloadResolverFactory resolverFactory,ClrMethod method, DynamicMetaObject target, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNullItems(args, "args"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); TargetInfo targetInfo = GetTargetInfo(method, target, args); if (targetInfo != null) { // we're calling a well-known MethodBase DynamicMetaObject res = MakeMetaMethodCall(signature, resolverFactory, targetInfo); if (res.Expression.Type.IsValueType) { if (res.Expression.Type == Types.Void) res = new DynamicMetaObject( Expression.Block(Types.Object[0], res.Expression, Expression.Constant(null)), res.Restrictions ); else res = new DynamicMetaObject( Expression.Convert(res.Expression, typeof(object)), res.Restrictions ); } return res; } else { // we can't call this object return errorSuggestion ?? MakeCannotCallRule(target, target.GetLimitType()); } }
public DynamicMetaObject ConvertTo(Type toType, ConversionResultKind kind, DynamicMetaObject arg, OverloadResolverFactory resolverFactory, DynamicMetaObject errorSuggestion) { ContractUtils.RequiresNotNull(toType, "toType"); ContractUtils.RequiresNotNull(arg, "arg"); Type knownType = arg.GetLimitType(); // try all the conversions - first look for conversions against the expression type, // these can be done w/o any additional tests. Then look for conversions against the // restricted type. BindingRestrictions typeRestrictions = arg.Restrictions.Merge(BindingRestrictionsHelpers.GetRuntimeTypeRestriction(arg.Expression, arg.GetLimitType())); DynamicMetaObject res = TryConvertToObject(toType, arg.Expression.Type, arg, typeRestrictions) ?? TryAllConversions(resolverFactory, toType, kind, arg.Expression.Type, typeRestrictions, arg) ?? TryAllConversions(resolverFactory, toType, kind, arg.GetLimitType(), typeRestrictions, arg) ?? errorSuggestion ?? MakeErrorTarget(toType, kind, typeRestrictions, arg); if ((kind == ConversionResultKind.ExplicitTry || kind == ConversionResultKind.ImplicitTry) && toType.IsValueType) { res = new DynamicMetaObject( AstUtils.Convert( res.Expression, typeof(object) ), res.Restrictions ); } return res; }
/// <summary> /// Checks if any conversions are available and if so builds the target for that conversion. /// </summary> private DynamicMetaObject TryAllConversions(OverloadResolverFactory factory, Type toType, ConversionResultKind kind, Type knownType, BindingRestrictions restrictions, DynamicMetaObject arg) { return TryAssignableConversion(toType, knownType, restrictions, arg) ?? // known type -> known type TryExtensibleConversion(toType, knownType, restrictions, arg) ?? // Extensible<T> -> Extensible<T>.Value TryUserDefinedConversion(kind, toType, knownType, restrictions, arg) ?? // op_Implicit TryImplicitNumericConversion(toType, knownType, restrictions, arg) ?? // op_Implicit TryNullableConversion(factory, toType, kind, knownType, restrictions, arg) ?? // null -> Nullable<T> or T -> Nullable<T> TryNullConversion(toType, knownType, restrictions); // null -> reference type }
/// <summary> /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod /// GetBoundMember, and StrongBox instances. /// </summary> /// <param name="name"> /// The name of the member to retrieve. This name is not processed by the DefaultBinder and /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... /// </param> /// <param name="target"> /// The MetaObject from which the member is retrieved. /// </param> /// <param name="resolverFactory"> /// Provides overload resolution and method binding for any calls which need to be performed for the GetMember. /// </param> /// <returns> /// Returns a DynamicMetaObject which represents the value that will be returned when the member is accessed. /// /// The returned DynamicMetaObject may be strongly typed to a value type which needs boxing before being /// returned from a standard DLR GetMemberBinder. The language is responsible for performing any boxing /// so that it has an opportunity to perform custom boxing. /// </returns> public DynamicMetaObject GetMember(string name, DynamicMetaObject target, OverloadResolverFactory resolverFactory) { return GetMember( name, target, resolverFactory, false, null ); }
public override Ast ConvertExpression(Ast expr, Type toType, ConversionResultKind kind, OverloadResolverFactory resolverFactory) { Type exprType = expr.Type; Type visType = CompilerHelpers.GetVisibleType(toType); if (typeof(IFn).IsAssignableFrom(exprType) && typeof(Delegate).IsAssignableFrom(visType)) return Ast.Call(typeof(Converter).GetMethod("ConvertToDelegate"), Ast.Convert(expr, typeof(IFn)), Expression.Constant(visType)); return base.ConvertExpression(expr, toType, kind, resolverFactory); }
/// <summary> /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod /// GetBoundMember, and StrongBox instances. /// </summary> /// <param name="name"> /// The name of the member to retrieve. This name is not processed by the DefaultBinder and /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... /// </param> /// <param name="target"> /// The MetaObject from which the member is retrieved. /// </param> /// <param name="value"> /// The value being assigned to the target member. /// </param> /// <param name="resolverFactory"> /// rovides overload resolution and method binding for any calls which need to be performed for the SetMember. /// </param> public DynamicMetaObject SetMember(string name, DynamicMetaObject target, DynamicMetaObject value, OverloadResolverFactory resolverFactory) { ContractUtils.RequiresNotNull(name, "name"); ContractUtils.RequiresNotNull(target, "target"); ContractUtils.RequiresNotNull(value, "value"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); return MakeSetMemberTarget( new SetOrDeleteMemberInfo(name, resolverFactory), target, value ); }
public DynamicMetaObject DeleteMember(string name, DynamicMetaObject target, OverloadResolverFactory resolutionFactory) { ContractUtils.RequiresNotNull(name, "name"); ContractUtils.RequiresNotNull(target, "target"); return MakeDeleteMemberTarget( new SetOrDeleteMemberInfo( name, resolutionFactory ), target.Restrict(target.GetLimitType()) ); }
/// <summary> /// Provides default binding for performing a call on the specified meta objects. /// </summary> /// <param name="signature">The signature describing the call</param> /// <param name="target">The meta object to be called.</param> /// <param name="args"> /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// </param> /// <param name="resolverFactory">Overload resolver factory.</param> /// <returns>A MetaObject representing the call or the error.</returns> public DynamicMetaObject Call(CallSignature signature, OverloadResolverFactory resolverFactory, DynamicMetaObject target, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNullItems(args, "args"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); TargetInfo targetInfo = GetTargetInfo(signature, target, args); if (targetInfo != null) { // we're calling a well-known MethodBase return MakeMetaMethodCall(signature, resolverFactory, targetInfo); } else { // we can't call this object return MakeCannotCallRule(target, target.GetLimitType()); } }
protected override DynamicMetaObject GetBoundValue(OverloadResolverFactory factory, ActionBinder binder, Type type, DynamicMetaObject instance) { return new DynamicMetaObject( Ast.Call( typeof(PythonOps).GetMethod("SlotGetValue"), ((PythonOverloadResolverFactory)factory)._codeContext, AstUtils.Constant(GetSlot(), typeof(PythonTypeSlot)), AstUtils.Convert( instance.Expression, typeof(object) ), AstUtils.Constant(DynamicHelpers.GetPythonTypeFromType(type)) ), BindingRestrictions.Empty ); }
/// <summary> /// Builds a MetaObject for performing a member get. Supports all built-in .NET members, the OperatorMethod /// GetBoundMember, and StrongBox instances. /// </summary> /// <param name="name"> /// The name of the member to retrieve. This name is not processed by the DefaultBinder and /// is instead handed off to the GetMember API which can do name mangling, case insensitive lookups, etc... /// </param> /// <param name="target"> /// The MetaObject from which the member is retrieved. /// </param> /// <param name="resolverFactory"> /// An OverloadResolverFactory which can be used for performing overload resolution and method binding. /// </param> /// <param name="isNoThrow"> /// True if the operation should return Operation.Failed on failure, false if it /// should return the exception produced by MakeMissingMemberError. /// </param> /// <param name="errorSuggestion"> /// The meta object to be used if the get results in an error. /// </param> public DynamicMetaObject GetMember(string name, DynamicMetaObject target, OverloadResolverFactory resolverFactory, bool isNoThrow, DynamicMetaObject errorSuggestion) { ContractUtils.RequiresNotNull(name, "name"); ContractUtils.RequiresNotNull(target, "target"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); return MakeGetMemberTarget( new GetMemberInfo( name, resolverFactory, isNoThrow, errorSuggestion ), target ); }
private DynamicMetaObject MakeMetaMethodCall(CallSignature signature, OverloadResolverFactory resolverFactory, TargetInfo targetInfo) { BindingRestrictions restrictions = BindingRestrictions.Combine(targetInfo.Arguments).Merge(targetInfo.Restrictions); if (targetInfo.Instance != null) { restrictions = targetInfo.Instance.Restrictions.Merge(restrictions); } DynamicMetaObject[] args; CallTypes callType; if (targetInfo.Instance != null) { args = ArrayUtils.Insert(targetInfo.Instance, targetInfo.Arguments); callType = CallTypes.ImplicitInstance; } else { args = targetInfo.Arguments; callType = CallTypes.None; } return CallMethod(resolverFactory.CreateOverloadResolver(args, signature, callType), targetInfo.Targets, restrictions); }
public override DynamicMetaObject GetValue(OverloadResolverFactory resolverFactory, ActionBinder binder, Type type) { if (!IsStatic || GetIndexParameters().Length > 0) { // need to bind to a value or parameters to get the value. return binder.ReturnMemberTracker(type, this); } MethodInfo getter = ResolveGetter(binder.PrivateBinding); if (getter == null || getter.ContainsGenericParameters) { // no usable getter return null; } if (getter.IsPublic && getter.DeclaringType.IsPublic) { return binder.MakeCallExpression(resolverFactory, getter); } // private binding is just a call to the getter method... return MemberTracker.FromMemberInfo(getter).Call(resolverFactory, binder); }
/// <summary> /// Called when the user is accessing a protected or private member on a get. /// /// The default implementation allows access to the fields or properties using reflection. /// </summary> public virtual ErrorInfo MakeNonPublicMemberGetError(OverloadResolverFactory resolverFactory, MemberTracker member, Type type, DynamicMetaObject instance) { switch (member.MemberType) { case TrackerTypes.Field: FieldTracker ft = (FieldTracker)member; return ErrorInfo.FromValueNoError( Ast.Call( AstUtils.Convert(AstUtils.Constant(ft.Field), typeof(FieldInfo)), typeof(FieldInfo).GetMethod("GetValue"), AstUtils.Convert(instance.Expression, typeof(object)) ) ); case TrackerTypes.Property: PropertyTracker pt = (PropertyTracker)member; return ErrorInfo.FromValueNoError( MemberTracker.FromMemberInfo(pt.GetGetMethod(true)).Call(resolverFactory, this, instance).Expression ); default: throw new InvalidOperationException(); } }
/// <summary> /// Provides default binding for performing a call on the specified meta objects. /// </summary> /// <param name="signature">The signature describing the call</param> /// <param name="target">The meta object to be called.</param> /// <param name="args"> /// Additional meta objects are the parameters for the call as specified by the CallSignature in the CallAction. /// </param> /// <param name="resolverFactory">Overload resolver factory.</param> /// <returns>A MetaObject representing the call or the error.</returns> public DynamicMetaObject Call(CallSignature signature, OverloadResolverFactory resolverFactory, DynamicMetaObject target, params DynamicMetaObject[] args) { ContractUtils.RequiresNotNullItems(args, "args"); ContractUtils.RequiresNotNull(resolverFactory, "resolverFactory"); TargetInfo targetInfo = GetTargetInfo(target, args); if (targetInfo != null) { // we're calling a well-known MethodBase DynamicMetaObject res = MakeMetaMethodCall(signature, resolverFactory, targetInfo); if (res.Expression.Type.IsValueType) { res = new DynamicMetaObject( AstUtils.Convert(res.Expression, typeof(object)), res.Restrictions ); } return res; } else { // we can't call this object return MakeCannotCallRule(target, target.GetLimitType()); } }
public virtual ErrorInfo MakeEventValidation(MemberGroup members, DynamicMetaObject eventObject, DynamicMetaObject value, OverloadResolverFactory resolverFactory) { EventTracker ev = (EventTracker)members[0]; // handles in place addition of events - this validates the user did the right thing. return ErrorInfo.FromValueNoError( Expression.Call( typeof(BinderOps).GetMethod("SetEvent"), AstUtils.Constant(ev), value.Expression ) ); }
private Expression ConvertIfNeeded(OverloadResolverFactory factory, Expression expression, Type type) { Assert.NotNull(expression, type); if (expression.Type != type) { return ConvertExpression(expression, type, ConversionResultKind.ExplicitCast, factory); } return expression; }
private DynamicMetaObject TryMakeInvertedBindingTarget(OverloadResolverFactory resolverFactory, MethodBase[] targets, DynamicMetaObject[] args) { var resolver = resolverFactory.CreateOverloadResolver(args, new CallSignature(args.Length), CallTypes.None); BindingTarget target = resolver.ResolveOverload(targets[0].Name, targets, NarrowingLevel.None, NarrowingLevel.All); if (target.Success) { return new DynamicMetaObject( Ast.Not(target.MakeExpression()), target.RestrictedArguments.GetAllRestrictions() ); } return null; }
private DynamicMetaObject MakeArrayIndexRule(OverloadResolverFactory factory, IndexType oper, DynamicMetaObject[] args) { if (CanConvertFrom(GetArgType(args, 1), typeof(int), false, NarrowingLevel.All)) { BindingRestrictions restrictions = BindingRestrictionsHelpers.GetRuntimeTypeRestriction(args[0].Expression, args[0].GetLimitType()).Merge(BindingRestrictions.Combine(args)); if (oper == IndexType.Get) { return new DynamicMetaObject( Ast.ArrayAccess( args[0].Expression, ConvertIfNeeded(factory, args[1].Expression, typeof(int)) ), restrictions ); } else { return new DynamicMetaObject( Ast.Assign( Ast.ArrayAccess( args[0].Expression, ConvertIfNeeded(factory, args[1].Expression, typeof(int)) ), ConvertIfNeeded(factory, args[2].Expression, args[0].GetLimitType().GetElementType()) ), restrictions.Merge(args[1].Restrictions) ); } } return null; }
private DynamicMetaObject MakeMethodIndexRule(IndexType oper, OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { MethodInfo[] defaults = GetMethodsFromDefaults(args[0].GetLimitType().GetDefaultMembers(), oper); if (defaults.Length != 0) { DynamicMetaObject[] selfWithArgs = args; ParameterExpression arg2 = null; if (oper == IndexType.Set) { Debug.Assert(args.Length >= 2); // need to save arg2 in a temp because it's also our result arg2 = Ast.Variable(args[2].Expression.Type, "arg2Temp"); args[2] = new DynamicMetaObject( Ast.Assign(arg2, args[2].Expression), args[2].Restrictions ); } BindingRestrictions restrictions = BindingRestrictions.Combine(args); var resolver = resolverFactory.CreateOverloadResolver(selfWithArgs, new CallSignature(selfWithArgs.Length), CallTypes.ImplicitInstance); BindingTarget target = resolver.ResolveOverload(oper == IndexType.Get ? "get_Item" : "set_Item", defaults, NarrowingLevel.None, NarrowingLevel.All); if (target.Success) { if (oper == IndexType.Get) { return new DynamicMetaObject( target.MakeExpression(), restrictions.Merge(target.RestrictedArguments.GetAllRestrictions()) ); } else { return new DynamicMetaObject( Ast.Block( new ParameterExpression[] { arg2 }, target.MakeExpression(), arg2 ), restrictions.Merge(target.RestrictedArguments.GetAllRestrictions()) ); } } return MakeError( resolver.MakeInvalidParametersError(target), restrictions, typeof(object) ); } return null; }
private DynamicMetaObject TryReverseOperator(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { // we need a special conversion for the return type on MemberNames if (args.Length > 0) { MethodInfo[] targets = GetApplicableMembers(args[0].LimitType, info); if (targets.Length > 0) { return TryMakeBindingTarget(resolverFactory, targets, args, BindingRestrictions.Empty); } } return null; }
private DynamicMetaObject TryForwardOperator(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { MethodInfo[] targets = GetApplicableMembers(args[0].GetLimitType(), info); BindingRestrictions restrictions = BindingRestrictions.Empty; if (targets.Length > 0) { return TryMakeBindingTarget(resolverFactory, targets, args, restrictions); } return null; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] // TODO: fix private DynamicMetaObject MakeOperatorRule(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { return TryForwardOperator(info, resolverFactory, args) ?? TryReverseOperator(info, resolverFactory, args) ?? TryPrimitiveOperator(info, args) ?? TryMakeDefaultUnaryRule(info, args) ?? MakeOperatorError(info, args); }
private DynamicMetaObject TryInvertedComparison(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject target, DynamicMetaObject[] args) { ExpressionType revOp = GetInvertedOperator(info.Operator); OperatorInfo revInfo = OperatorInfo.GetOperatorInfo(revOp); Debug.Assert(revInfo != null); // try the 1st type's opposite function result negated MethodBase[] targets = GetApplicableMembers(target.GetLimitType(), revInfo); if (targets.Length > 0) { return TryMakeInvertedBindingTarget(resolverFactory, targets, args); } return null; }
private DynamicMetaObject TryNumericComparison(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject[] args) { MethodInfo[] targets = FilterNonMethods( args[0].GetLimitType(), GetMember( MemberRequestKind.Operation, args[0].GetLimitType(), "Compare" ) ); if (targets.Length > 0) { var resolver = resolverFactory.CreateOverloadResolver(args, new CallSignature(args.Length), CallTypes.None); BindingTarget target = resolver.ResolveOverload(targets[0].Name, targets, NarrowingLevel.None, NarrowingLevel.All); if (target.Success) { Expression call = AstUtils.Convert(target.MakeExpression(), typeof(int)); switch (info.Operator) { case ExpressionType.GreaterThan: call = Ast.GreaterThan(call, AstUtils.Constant(0)); break; case ExpressionType.LessThan: call = Ast.LessThan(call, AstUtils.Constant(0)); break; case ExpressionType.GreaterThanOrEqual: call = Ast.GreaterThanOrEqual(call, AstUtils.Constant(0)); break; case ExpressionType.LessThanOrEqual: call = Ast.LessThanOrEqual(call, AstUtils.Constant(0)); break; case ExpressionType.Equal: call = Ast.Equal(call, AstUtils.Constant(0)); break; case ExpressionType.NotEqual: call = Ast.NotEqual(call, AstUtils.Constant(0)); break; } return new DynamicMetaObject( call, target.RestrictedArguments.GetAllRestrictions() ); } } return null; }
private DynamicMetaObject TryComparisonMethod(OperatorInfo info, OverloadResolverFactory resolverFactory, DynamicMetaObject target, DynamicMetaObject[] args) { MethodInfo[] targets = GetApplicableMembers(target.GetLimitType(), info); if (targets.Length > 0) { return TryMakeBindingTarget(resolverFactory, targets, args, BindingRestrictions.Empty); } return null; }