From 0819cdcf926fe7d54e85cbee440c2b334676b2b3 Mon Sep 17 00:00:00 2001 From: jpbruyere Date: Thu, 15 Dec 2016 18:32:52 +0100 Subject: [PATCH] =?utf8?q?emit=20bind=20datasource=20handler=20methods=20?= =?utf8?q?=09modifi=C3=A9=C2=A0:=20=20=20=20=20=20=20=20=20src/CompilerSer?= =?utf8?q?vices/CompilerServices.cs=20=09modifi=C3=A9=C2=A0:=20=20=20=20?= =?utf8?q?=20=20=20=20=20src/IML/Context.cs=20=09modifi=C3=A9=C2=A0:=20=20?= =?utf8?q?=20=20=20=20=20=20=20src/Instantiator.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- src/CompilerServices/CompilerServices.cs | 7 ++ src/IML/Context.cs | 8 ++ src/Instantiator.cs | 105 ++++++++++++++++++++--- 3 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 731d1b5e..ba824c62 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -28,6 +28,7 @@ namespace Crow public static MethodInfo miCreateExpDel = typeof(ItemTemplate).GetMethod ("CreateExpandDelegate"); public static MethodInfo miLoadDefaultVals = typeof (GraphicObject).GetMethod ("loadDefaultValues"); public static PropertyInfo piStyle = typeof (GraphicObject).GetProperty ("Style"); + public static MethodInfo miGetTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle"); #region tree handling methods internal static MethodInfo miSetChild = typeof (Container).GetMethod ("SetChild"); internal static MethodInfo miAddChild = typeof (Group).GetMethod ("AddChild"); @@ -635,6 +636,12 @@ namespace Crow public static MemberInfo getMemberInfoWithReflexion(object instance, string member){ return instance.GetType ().GetMember (member).FirstOrDefault(); } + public static MethodInfo getMethodInfoWithReflexion(object instance, string method){ + return instance.GetType ().GetMethod (method, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + } + public static Type getEventHandlerType(object instance, string eventName){ + return instance.GetType ().GetEvent (eventName).EventHandlerType; + } public static object getValueWithReflexion(object instance, MemberInfo mi){ object tmp = null; Type dstType = null; diff --git a/src/IML/Context.cs b/src/IML/Context.cs index 4f9ba30d..79ab8a31 100644 --- a/src/IML/Context.cs +++ b/src/IML/Context.cs @@ -83,5 +83,13 @@ namespace Crow.IML CompilerServices.emitSetCurInterface (il); } + public void emitDataSourceChangedHandlerAddition(int index){ + il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object + il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack + il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dataSourceChangedDelegates", BindingFlags.Instance | BindingFlags.NonPublic)); + il.Emit(OpCodes.Ldc_I4, index);//load delegate index + il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); + il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetEvent("DataSourceChanged").AddMethod);//call add event + } } } \ No newline at end of file diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 7db2967f..dcf53259 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -251,15 +251,21 @@ namespace Crow MemberInfo mi = ctx.CurrentNodeType.GetMember (reader.Name).FirstOrDefault (); if (mi == null) throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); + if (mi.MemberType == MemberTypes.Event) { - if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) - emitEventHandler(ctx, mi as EventInfo,reader.Value.Substring (1, reader.Value.Length - 2)); - //else //emit handler binding + foreach (string exp in splitOnSemiColumnOutsideAccolades(reader.Value)) { + string trimed = exp.Trim(); + if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) + emitEventHandler (ctx, mi as EventInfo, trimed.Substring (1, trimed.Length - 2)); + else + emitHandlerBinding (ctx, mi as EventInfo, trimed); + } + continue; } PropertyInfo pi = mi as PropertyInfo; if (pi == null) - throw new Exception ("Member '" + reader.Name + "' not found in " + ctx.CurrentNodeType.Name); + throw new Exception ("Member '" + reader.Name + "' is not a property in " + ctx.CurrentNodeType.Name); if (pi.Name == "Name"){ if (!ctx.Names.ContainsKey(reader.Value)) @@ -282,6 +288,30 @@ namespace Crow ctx.nodesStack.ResetCurrentNodeIndex (); } } + string[] splitOnSemiColumnOutsideAccolades (string expression){ + List exps = new List(); + int accCount = 0; + int expPtr = 0; + for (int c = 0; c < expression.Length; c++) { + switch (expression[c]){ + case '{': + accCount++; + break; + case '}': + accCount--; + break; + case ';': + if (accCount > 0) + break; + exps.Add(expression.Substring(expPtr, c - expPtr - 1)); + expPtr = c + 1; + break; + } + } + if (exps.Count == 0) + exps.Add(expression); + return exps.ToArray (); + } /// /// Parse child node an generate corresponding msil /// @@ -560,19 +590,18 @@ namespace Crow dataSourceChangedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); #endregion - #region Emit datasourcechanged handler binding in the loader context - ctx.il.Emit(OpCodes.Ldloc_0);//load ref to current graphic object - ctx.il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack - ctx.il.Emit(OpCodes.Ldfld, typeof(Instantiator).GetField("dataSourceChangedDelegates", BindingFlags.Instance | BindingFlags.NonPublic)); - ctx.il.Emit(OpCodes.Ldc_I4, delDSIndex);//load delegate index - ctx.il.Emit(OpCodes.Callvirt, typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) })); - ctx.il.Emit(OpCodes.Callvirt, typeof(GraphicObject).GetEvent("DataSourceChanged").AddMethod);//call add event - #endregion + //Emit datasourcechanged handler binding in the loader context + ctx.emitDataSourceChangedHandlerAddition(delDSIndex); } + + #region Emit Helper void dataSourceChangedEmitHelper(object dscSource, IValueChange dataSource, int dynMethIdx){ dataSource.ValueChanged += (EventHandler)dsValueChangedDynMeths [dynMethIdx].CreateDelegate (typeof(EventHandler), dscSource); } + #endregion + + /// /// Compile events expression in IML attributes, and store the result in the instanciator /// Those handlers will be bound when instatiing @@ -714,6 +743,58 @@ namespace Crow #endregion } + void emitHandlerBinding (Context ctx, EventInfo sourceEvent, string expression){ + string[] bindingExp = expression.Split ('/'); + + if (bindingExp.Length == 1) { + //datasource handler + //we need to bind datasource method to source event + DynamicMethod dm = new DynamicMethod ("dyn_dschangedForHandler", + typeof (void), + CompilerServices.argsDSChange, true); + + ILGenerator il = dm.GetILGenerator (256); + + il.DeclareLocal (typeof(MethodInfo));//used to cancel binding if method doesn't exist + + il.Emit (OpCodes.Nop); + + //fetch method in datasource and test if it exist + il.Emit (OpCodes.Ldarg_2);//load new datasource + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldstr, bindingExp[0]);//load handler method name + il.Emit (OpCodes.Call, typeof(CompilerServices).GetMethod("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.Public)); + il.Emit (OpCodes.Stloc_0);//save MethodInfo + il.Emit (OpCodes.Ldloc_0);//push mi for test if null + System.Reflection.Emit.Label methodNotFound = il.DefineLabel (); + il.Emit (OpCodes.Brfalse, methodNotFound); + + il.Emit (OpCodes.Ldarg_1);//load datasource change source where the event handler is as 1st arg of handler.add + + //loat handlerType of sourceEvent to create delegate (1st arg) + il.Emit(OpCodes.Ldtoken, sourceEvent.EventHandlerType); + il.Emit (OpCodes.Call, CompilerServices.miGetTypeFromHandle); + il.Emit (OpCodes.Ldarg_2);//load new datasource where the method is defined + il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); + il.Emit (OpCodes.Ldloc_0);//load methodInfo (3rd arg) + + il.Emit (OpCodes.Callvirt, typeof(Delegate).GetMethod ("CreateDelegate", + new Type[] {typeof(Type), typeof(object), typeof(MethodInfo)}));//create bound delegate + il.Emit(OpCodes.Callvirt, sourceEvent.AddMethod);//call add event + + il.MarkLabel(methodNotFound); + il.Emit (OpCodes.Ret); + + //store dschange delegate in instatiator instance for access while instancing graphic object + int delDSIndex = dataSourceChangedDelegates.Count; + dataSourceChangedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); + + //Emit datasourcechanged handler binding in the loader context + ctx.emitDataSourceChangedHandlerAddition(delDSIndex); + return; + } + } + /// /// Create and store in the instanciator the ValueChanged delegates /// those delegates uses grtree functions to set destination value so they don't -- 2.47.3