/// <summary> /// Creates a default allocator for the pooled type. /// </summary> /// <returns>The allocator that was created.</returns> protected static Func <T> CreateDefaultAllocator() { var ctor = typeof(T).GetConstructor(Type.EmptyTypes); if (ctor == null) { throw new InvalidOperationException(CoreStrings.MissingDefaultCtor.Format(typeof(T).FullName)); } if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { return(Expression.Lambda <Func <T> >(Expression.New(typeof(T))).Compile()); } else { return(() => (T)ctor.Invoke(null)); } }
/// <summary> /// Registers a default interpolator for the specified type. /// </summary> /// <typeparam name="T">The type for which to register a default interpolator.</typeparam> public void RegisterDefault <T>() { var interpolator = default(Interpolator <T>); if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { var interpolateMethod = typeof(T).GetMethod("Interpolate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(T), typeof(Single) }, null); if (interpolateMethod != null) { var paramValueStart = Expression.Parameter(typeof(T), "valueStart"); var paramValueEnd = Expression.Parameter(typeof(T), "valueEnd"); var paramFn = Expression.Parameter(typeof(EasingFunction), "fn"); var paramT = Expression.Parameter(typeof(Single), "t"); var expInvokeFn = Expression.Invoke(Expression.Coalesce(paramFn, Expression.Constant(Easings.EaseInLinear)), paramT); var expLambdaBody = Expression.Call(paramValueStart, interpolateMethod, paramValueEnd, expInvokeFn); interpolator = Expression.Lambda <Interpolator <T> >(expLambdaBody, paramValueStart, paramValueEnd, paramFn, paramT).Compile(); } } else { if (typeof(IInterpolatable <T>).IsAssignableFrom(typeof(T))) { // Invoke through interface interpolator = (valueStart, valueEnd, fn, t) => ((IInterpolatable <T>)valueStart).Interpolate(valueEnd, (fn ?? Easings.EaseInLinear)(t)); } else { // Invoke through pattern var interpolateMethod = typeof(T).GetMethod("Interpolate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(T), typeof(Single) }, null); if (interpolateMethod != null) { interpolator = (valueStart, valueEnd, fn, t) => (T)interpolateMethod.Invoke(valueStart, new Object[] { valueEnd, fn, t }); } } } Register(interpolator); }
/// <summary> /// Initializes a new instance of the <see cref="DataBindingGetterBuilder"/> class. /// </summary> /// <param name="expressionType">The type of the bound expression.</param> /// <param name="dataSourceType">The type of the data source to which the expression is being bound.</param> /// <param name="expression">The binding expression with which to bind the dependency property.</param> public DataBindingGetterBuilder(Type expressionType, Type dataSourceType, String expression) : base(dataSourceType) { this.delegateType = typeof(DataBindingGetter <>).MakeGenericType(expressionType); if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { CreateReturnTarget(expressionType); var path = BindingExpressions.GetBindingMemberPathPart(expression); var pathParts = path.Contains(".") ? path.Split('.') : null; var pathFinalPart = (pathParts == null) ? path : pathParts[pathParts.Length - 1]; var current = AddDataSourceReference(); if (pathParts != null) { for (int i = 0; i < pathParts.Length - 1; i++) { current = AddSafeReference(expression, current, pathParts[i]); } } current = AddSafeReference(expression, current, pathFinalPart); var result = Expression.Convert(current, expressionType); AddReturn(result); AddReturnLabel(); var lambdaBody = Expression.Block(variables, expressions); var lambda = Expression.Lambda(delegateType, lambdaBody, parameters); lambdaExpression = lambda; } else { var expParamDataSource = Expression.Parameter(typeof(Object), "dataSource"); var implMethod = typeof(DataBindingGetterBuilder).GetMethod(nameof(ReflectionBasedImplementation), BindingFlags.NonPublic | BindingFlags.Static); var path = BindingExpressions.GetBindingMemberPathPart(expression); var pathParts = path.Contains(".") ? path.Split('.') : null; var pathFinalPart = (pathParts == null) ? path : pathParts[pathParts.Length - 1]; var expDataSource = (Expression)Expression.Convert(expParamDataSource, dataSourceType); var expDataSourceType = dataSourceType; var current = dataSourceType; if (pathParts != null) { for (int i = 0; i < pathParts.Length - 1; i++) { expDataSourceType = BindingExpressions.GetMemberType(BindingExpressions.FindPropertyOrField(expDataSourceType, pathParts[i])); expDataSource = Expression.PropertyOrField(expDataSource, pathParts[i]); } } var member = BindingExpressions.FindPropertyOrField(expDataSourceType, pathFinalPart); if (!BindingExpressions.CanReadMember(member)) { return; } var expImplMethodCall = Expression.Call(implMethod, Expression.Constant(member), Expression.Convert(expDataSource, typeof(Object))); lambdaExpression = Expression.Lambda(delegateType, Expression.Convert( Expression.Convert(expImplMethodCall, BindingExpressions.GetMemberType(member)), expressionType), expParamDataSource); } }
/// <summary> /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Bubble"/> routing strategy. /// </summary> /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param> /// <returns>The invocation delegate for the specified routed event.</returns> private static Delegate CreateInvocationDelegateForBubbleStrategy(RoutedEvent evt) { /* BUBBLE STRATEGY * For a given event delegate type TDelegate, we're constructing a method which basically looks like this: * * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data) * { * var index = 0; * var handler = default(RoutedEventHandlerMetadata); * var current = element; * * while (ShouldContinueBubbling(element, ref current)) * { * index = 0; * while (GetEventHandler(current, RoutedEventID, ref index, ref handler)) * { * if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo)) * { * handler.Handler(current, p1, p2, ..., pN, data); * } * } * * RoutedEventID.RaiseRaisedNotification(current, data); * } * * if (data.AutoRelease) * data.Release(); * } */ if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { var evtInvoke = evt.DelegateType.GetMethod("Invoke"); var evtParams = evtInvoke.GetParameters().ToArray(); var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList(); var expParamElement = expParams.First(); var expParamData = expParams.Last(); var expParts = new List <Expression>(); var expVars = new List <ParameterExpression>(); var varIndex = Expression.Variable(typeof(Int32), "index"); expVars.Add(varIndex); var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handlers"); expVars.Add(varHandler); var varCurrent = Expression.Variable(typeof(DependencyObject), "current"); expVars.Add(varCurrent); var innerEventHandlerParams = new List <ParameterExpression>(); innerEventHandlerParams.Add(varCurrent); innerEventHandlerParams.AddRange(expParams.Skip(1)); var expWhileBubbleBreakOuter = Expression.Label(); var expWhileBubbleBreakInner = Expression.Label(); var expWhileBubble = Expression.Loop( Expression.IfThenElse( Expression.Call(miShouldContinueBubbling, expParamElement, varCurrent), Expression.Block( Expression.Assign(varIndex, Expression.Constant(0)), Expression.Loop( Expression.IfThenElse( Expression.Call(miGetEventHandler, varCurrent, Expression.Constant(evt), varIndex, varHandler), Expression.IfThen( Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")), Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), innerEventHandlerParams) ), Expression.Break(expWhileBubbleBreakInner) ), expWhileBubbleBreakInner ), Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, varCurrent, expParamData) ), Expression.Break(expWhileBubbleBreakOuter) ), expWhileBubbleBreakOuter ); expParts.Add(expWhileBubble); expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))), Expression.Call(expParamData, nameof(RoutedEventData.Release), null))); return(Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile()); } else { return(CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForBubbleStrategy))); } }
/// <summary> /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Tunnel"/> routing strategy. /// </summary> /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param> /// <returns>The invocation delegate for the specified routed event.</returns> private static Delegate CreateInvocationDelegateForTunnelStrategy(RoutedEvent evt) { /* TUNNEL STRATEGY * Basically the opposite of the bubble strategy; we start at the root of the tree and work down. * Note that ShouldContinueTunnelling() builds a stack representing the path to take on the first call. * * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data) * { * var index = 0; * var current = default(DependencyObject); * var handlers = default(List<RoutedEventHandlerMetadata>); * var stack = default(Stack<DependencyObject>); * * while (ShouldContinueTunnelling(element, ref current, ref stack)) * { * index = 0; * while (GetEventHandler(current, RoutedEventID, index, out handler)) * { * if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo)) * { * handler.Handler(current, p1, p2, ..., pN, data); * } * } * * RoutedEventID.RaiseRaisedNotification(current, data); * } * * ReleaseTunnellingStack(stack); * * if (data.AutoRelease) * data.Release(); * } */ if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { var evtInvoke = evt.DelegateType.GetMethod("Invoke"); var evtParams = evtInvoke.GetParameters().ToArray(); var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList(); var expParamElement = expParams.First(); var expParamData = expParams.Last(); var expParts = new List <Expression>(); var expVars = new List <ParameterExpression>(); var varIndex = Expression.Variable(typeof(Int32), "index"); expVars.Add(varIndex); var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handler"); expVars.Add(varHandler); var varCurrent = Expression.Variable(typeof(DependencyObject), "current"); expVars.Add(varCurrent); var varStack = Expression.Variable(typeof(Stack <DependencyObject>), "stack"); expVars.Add(varStack); var innerEventHandlerParams = new List <ParameterExpression>(); innerEventHandlerParams.Add(varCurrent); innerEventHandlerParams.AddRange(expParams.Skip(1)); var expWhileTunnelBreakOuter = Expression.Label(); var expWhileTunnelBreakInner = Expression.Label(); var expWhileTunnel = Expression.Loop( Expression.IfThenElse( Expression.Call(miShouldContinueTunnelling, expParamElement, varCurrent, varStack), Expression.Block( Expression.Assign(varIndex, Expression.Constant(0)), Expression.Loop( Expression.IfThenElse( Expression.Call(miGetEventHandler, varCurrent, Expression.Constant(evt), varIndex, varHandler), Expression.IfThen( Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")), Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), innerEventHandlerParams) ), Expression.Break(expWhileTunnelBreakInner) ), expWhileTunnelBreakInner ), Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, varCurrent, expParamData) ), Expression.Break(expWhileTunnelBreakOuter) ), expWhileTunnelBreakOuter ); expParts.Add(expWhileTunnel); expParts.Add(Expression.Call(miReleaseTunnellingStack, varStack)); expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))), Expression.Call(expParamData, nameof(RoutedEventData.Release), null))); return(Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile()); } else { return(CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForTunnelStrategy))); } }
/// <summary> /// Creates an invocation delegate for a routed event which uses the <see cref="RoutingStrategy.Direct"/> routing strategy. /// </summary> /// <param name="evt">A <see cref="RoutedEvent"/> which identifies the routed event for which to create an invocation delegate.</param> /// <returns>The invocation delegate for the specified routed event.</returns> private static Delegate CreateInvocationDelegateForDirectStrategy(RoutedEvent evt) { /* DIRECT STRATEGY * The simplest strategy; the event is only invoked on the element that raised it. * Our invocation delegate looks something like this: * * void fn(DependencyObject element, p1, p2, ..., pN, RoutedEventData data) * { * var index = 0; * var handler = default(RoutedEventHandlerMetadata); * * while (GetEventHandler(element, RoutedEventID, ref index, ref handler)) * { * if (ShouldEventBeRaisedForElement(data, handler.HandledEventsToo)) * { * handler.Handler(element, p1, p2, ..., pN, data); * } * } * * RoutedEventID.RaiseRaisedNotification(element, data); * * if (data.AutoRelease) * data.Release(); * } */ if (UltravioletPlatformInfo.IsRuntimeCodeGenerationSupported()) { var evtInvoke = evt.DelegateType.GetMethod("Invoke"); var evtParams = evtInvoke.GetParameters().ToArray(); var expParams = evtParams.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToList(); var expParamElement = expParams.First(); var expParamData = expParams.Last(); var expParts = new List <Expression>(); var expVars = new List <ParameterExpression>(); var varIndex = Expression.Variable(typeof(Int32), "index"); expVars.Add(varIndex); var varHandler = Expression.Variable(typeof(RoutedEventHandlerMetadata), "handlers"); expVars.Add(varHandler); var expInvokeBreak = Expression.Label(); var expInvoke = Expression.Loop( Expression.IfThenElse( Expression.Call(miGetEventHandler, expParamElement, Expression.Constant(evt), varIndex, varHandler), Expression.IfThen( Expression.Call(miShouldEventBeRaisedForElement, expParamData, Expression.Property(varHandler, "HandledEventsToo")), Expression.Invoke(Expression.Convert(Expression.Property(varHandler, "Handler"), evt.DelegateType), expParams) ), Expression.Break(expInvokeBreak) ), expInvokeBreak ); expParts.Add(expInvoke); var expRaiseRaised = Expression.Call(Expression.Constant(evt), miRaiseRaisedNotification, expParamElement, expParamData); expParts.Add(expRaiseRaised); expParts.Add(Expression.IfThen(Expression.IsTrue(Expression.Property(expParamData, nameof(RoutedEventData.AutoRelease))), Expression.Call(expParamData, nameof(RoutedEventData.Release), null))); return(Expression.Lambda(evt.DelegateType, Expression.Block(expVars, expParts), expParams).Compile()); } else { return(CreateDelegateForReflectionBasedImplementation(evt, nameof(ReflectionBasedImplementationForDirectStrategy))); } }