]> O.S.I.I.S - jp/crow.git/commitdiff
emit bind datasource handler methods
authorjpbruyere <jp.bruyere@hotmail.com>
Thu, 15 Dec 2016 17:32:52 +0000 (18:32 +0100)
committerjpbruyere <jp.bruyere@hotmail.com>
Thu, 15 Dec 2016 17:32:52 +0000 (18:32 +0100)
modifié :         src/CompilerServices/CompilerServices.cs
modifié :         src/IML/Context.cs
modifié :         src/Instantiator.cs

src/CompilerServices/CompilerServices.cs
src/IML/Context.cs
src/Instantiator.cs

index 731d1b5e96b41a64da186beb714f35247dfade6e..ba824c621f0f875e70bb48d07340d0848217f22c 100644 (file)
@@ -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;
index 4f9ba30d3e82040323ada9b15dff42ad5fbb864f..79ab8a31c3314907dbd6db92c88d74382e74e6da 100644 (file)
@@ -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<Delegate>).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
index 7db2967fa8bc28f8bf2a8059fac44ffd8933f7bd..dcf53259e56cbe59410b906d91fa1d9884097a07 100644 (file)
@@ -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<String> exps = new List<string>();
+                       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 ();
+               }
                /// <summary>
                /// Parse child node an generate corresponding msil
                /// </summary>
@@ -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<Delegate>).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<ValueChangeEventArgs>)dsValueChangedDynMeths [dynMethIdx].CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>), dscSource);
                }
+               #endregion
+
+
                /// <summary>
                /// 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;
+                       }
+               }
+
                /// <summary>
                /// Create and store in the instanciator the ValueChanged delegates
                /// those delegates uses grtree functions to set destination value so they don't