From 6872058571fff26386ab7fcf9ac497e1b651a42a Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Wed, 18 Jan 2017 15:19:37 +0100 Subject: 'this' keyword in compiled handler --- src/CompilerServices/CompilerServices.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/CompilerServices') diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index e0d6c1e0..e2c657ce 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -667,6 +667,8 @@ namespace Crow il.Emit (OpCodes.Ldstr, strcst); + }else if (string.Equals(rop,"this",StringComparison.OrdinalIgnoreCase)){ + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack } else { if (lopT.IsEnum) throw new NotImplementedException (); -- cgit v1.3.1-4-g156e From 2b9e8c8dbbe55a2297340deb2fd685bbf881e153 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Fri, 20 Jan 2017 08:16:44 +0100 Subject: code clean and comment --- src/CompilerServices/CompilerServices.cs | 277 ++++++++++++++++++++++++------- src/GraphicObjects/GraphicObject.cs | 3 +- src/Instantiator.cs | 16 +- 3 files changed, 226 insertions(+), 70 deletions(-) (limited to 'src/CompilerServices') diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index e2c657ce..9ddf9991 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -32,15 +32,15 @@ namespace Crow internal static MethodInfo miGetColCount = typeof(System.Collections.ICollection).GetProperty("Count").GetGetMethod(); internal static MethodInfo miGetDelegateListItem = typeof(List).GetMethod("get_Item", new Type[] { typeof(Int32) }); - internal static MethodInfo miCompileDynEventHandler = typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miRemEvtHdlByName = typeof(CompilerServices).GetMethod("RemoveEventHandlerByName", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miRemEvtHdlByTarget = typeof(CompilerServices).GetMethod("RemoveEventHandlerByTarget", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miGetMethInfoWithRefx = typeof(CompilerServices).GetMethod ("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miGetMembIinfoWithRefx = typeof(CompilerServices).GetMethod("getMemberInfoWithReflexion", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miSetValWithRefx = typeof(CompilerServices).GetMethod("setValueWithReflexion", BindingFlags.Static | BindingFlags.Public); - internal static MethodInfo miGetValWithRefx = typeof(CompilerServices).GetMethod("getValueWithReflexion", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miCompileDynEventHandler = typeof(CompilerServices).GetMethod ("compileDynEventHandler", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miRemEvtHdlByName = typeof(CompilerServices).GetMethod("removeEventHandlerByName", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miRemEvtHdlByTarget = typeof(CompilerServices).GetMethod("removeEventHandlerByTarget", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetMethInfoWithRefx = typeof(CompilerServices).GetMethod ("getMethodInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetMembIinfoWithRefx = typeof(CompilerServices).GetMethod("getMemberInfoWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miSetValWithRefx = typeof(CompilerServices).GetMethod("setValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); + internal static MethodInfo miGetValWithRefx = typeof(CompilerServices).GetMethod("getValueWithReflexion", BindingFlags.Static | BindingFlags.NonPublic); internal static MethodInfo miCreateDel = typeof(CompilerServices).GetMethod ("createDel", BindingFlags.Static | BindingFlags.NonPublic); - internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.Public); + internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.NonPublic); internal static FieldInfo fiCachedDel = typeof(Instantiator).GetField("cachedDelegates", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo fiTemplateBinding = typeof(Instantiator).GetField("templateBinding", BindingFlags.Instance | BindingFlags.NonPublic); @@ -236,32 +236,14 @@ namespace Crow } #endregion + #region Reflexion helpers /// - /// retrieve event handler in class or ancestors - /// - public static FieldInfo GetEventHandlerField (Type type, string eventName) - { - FieldInfo fi; - Type ty = type; - do { - fi = ty.GetField (eventName, - BindingFlags.NonPublic | - BindingFlags.Instance | - BindingFlags.GetField); - ty = ty.BaseType; - if (ty == null) - break; - } while (fi == null); - return fi; - } - - /// - /// Gets extension methods defined in assembley for extendedType + /// Gets all extension methods defined in assembly for Type /// /// Extension methods enumerable /// Assembly /// Extended type to search for - public static IEnumerable GetExtensionMethods (Assembly assembly, + internal static IEnumerable GetExtensionMethods (Assembly assembly, Type extendedType) { IEnumerable query = null; @@ -270,11 +252,11 @@ namespace Crow do { query = from type in assembly.GetTypes () where type.IsSealed && !type.IsGenericType && !type.IsNested - from method in type.GetMethods (BindingFlags.Static - | BindingFlags.Public | BindingFlags.NonPublic) + from method in type.GetMethods (BindingFlags.Static + | BindingFlags.Public | BindingFlags.NonPublic) where method.IsDefined (typeof (ExtensionAttribute), false) where method.GetParameters () [0].ParameterType == curType - select method; + select method; if (query.Count () > 0) break; @@ -284,8 +266,13 @@ namespace Crow return query; } - - public static MethodInfo SearchExtMethod(Type t, string methodName){ + /// + /// search for extentions method in entry assembly then in crow assembly + /// + /// Extention MethodInfo + /// Extended type + /// Extention method name + internal static MethodInfo SearchExtMethod(Type t, string methodName){ MethodInfo mi = null; mi = GetExtensionMethods (Assembly.GetEntryAssembly(), t) .Where (em => em.Name == methodName).FirstOrDefault (); @@ -295,17 +282,39 @@ namespace Crow return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) .Where (em => em.Name == methodName).FirstOrDefault (); } + /// + /// retrieve event handler in class or ancestors + /// + static FieldInfo getEventHandlerField (Type type, string eventName) + { + FieldInfo fi; + Type ty = type; + do { + fi = ty.GetField (eventName, + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.GetField); + ty = ty.BaseType; + if (ty == null) + break; + } while (fi == null); + return fi; + } + - public static MemberInfo getMemberInfoWithReflexion(object instance, string member){ - return instance.GetType ().GetMember (member).FirstOrDefault(); + static MemberInfo getMemberInfoWithReflexion(object instance, string member){ + return instance.GetType ().GetMember (member)?.FirstOrDefault(); } - public static MethodInfo getMethodInfoWithReflexion(object instance, string method){ + 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 void setValueWithReflexion(object dest, object value, string destMember){ + /// + /// set value, convert if required + /// + /// Destination instance + /// Value + /// Destination member + static void setValueWithReflexion(object dest, object value, string destMember){ Type destType = null; Type origType = null; object convertedVal = null; @@ -337,7 +346,11 @@ namespace Crow else if (miDest.MemberType == MemberTypes.Field) (miDest as FieldInfo).SetValue (dest, convertedVal); } - public static object getValueWithReflexion(object instance, MemberInfo mi){ + /// + /// Gets value with reflexion, return empty string ("") for string and object and return + /// default value for valueType data. + /// + static object getValueWithReflexion(object instance, MemberInfo mi){ object tmp = null; Type dstType = null; if (mi == null) @@ -361,7 +374,12 @@ namespace Crow return null; } - public static void emitGetInstance (ILGenerator il, NodeAddress orig, NodeAddress dest){ + #endregion + + /// + /// Emits tree parsing command to fetch dest instance starting from orig node + /// + internal static void emitGetInstance (ILGenerator il, NodeAddress orig, NodeAddress dest){ int ptr = 0; while (orig [ptr] == dest [ptr]) { ptr++; @@ -375,13 +393,24 @@ namespace Crow ptr++; } } - public static void emitGetInstance (ILGenerator il, NodeAddress dest){ + /// + /// Emits tree parsing commands to get child starting at root node + /// + /// MSIL generator + /// Absolute Node Address of the instance to get + internal static void emitGetInstance (ILGenerator il, NodeAddress dest){ if (dest == null) return; for (int i = 0; i < dest.Count - 1; i++) emitGetChild (il, dest [i].CrowType, dest [i + 1].Index); } - public static void emitGetChild(ILGenerator il, Type parentType, int index){ + /// + /// Emits msil to fetch chil instance of current GraphicObject on the stack + /// + /// Il generator + /// Parent type + /// Index of child, -1 for template root + internal static void emitGetChild(ILGenerator il, Type parentType, int index){ if (typeof (Group).IsAssignableFrom (parentType)) { il.Emit (OpCodes.Ldfld, fiChildren); il.Emit(OpCodes.Ldc_I4, index); @@ -404,9 +433,9 @@ namespace Crow } } /// - /// Emit conversion from orig type to dest type + /// Emit MSIL for conversion from orig type to dest type /// - public static void emitConvert(ILGenerator il, Type origType, Type destType){ + internal static void emitConvert(ILGenerator il, Type origType, Type destType){ if (destType == CompilerServices.TObject) return; if (destType == typeof(string)) { @@ -439,7 +468,7 @@ namespace Crow /// check type of current object on the stack and convert to dest type, /// use loc_0 so store it as object!!! /// - public static void emitConvert(ILGenerator il, Type dstType){ + internal static void emitConvert(ILGenerator il, Type dstType){ System.Reflection.Emit.Label endConvert = il.DefineLabel (); System.Reflection.Emit.Label convert = il.DefineLabel (); @@ -503,7 +532,7 @@ namespace Crow /// /// search for an implicit conversion method in origine or destination classes /// - public static MethodInfo getImplicitOp(Type origType, Type dstType){ + static MethodInfo getImplicitOp(Type origType, Type dstType){ foreach(MethodInfo mi in origType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ if (mi.Name == "op_Implicit") { if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) @@ -521,9 +550,9 @@ namespace Crow /// /// Removes delegate from event handler by name /// - public static void RemoveEventHandlerByName(object instance, string eventName, string delegateName){ + static void removeEventHandlerByName(object instance, string eventName, string delegateName){ Type t = instance.GetType (); - FieldInfo fiEvt = CompilerServices.GetEventHandlerField (t, eventName); + FieldInfo fiEvt = getEventHandlerField (t, eventName); if (fiEvt == null) { Debug.WriteLine ("RemoveHandlerByName: Event '" + eventName + "' not found in " + instance); return; @@ -544,9 +573,9 @@ namespace Crow /// /// Removes delegate from event handler by searching for the object they are bond to /// - public static void RemoveEventHandlerByTarget(object instance, string eventName, object target){ + static void removeEventHandlerByTarget(object instance, string eventName, object target){ Type t = instance.GetType (); - FieldInfo fiEvt = CompilerServices.GetEventHandlerField (t, eventName); + FieldInfo fiEvt = getEventHandlerField (t, eventName); EventInfo eiEvt = t.GetEvent (eventName); MulticastDelegate multiDel = fiEvt.GetValue (instance) as MulticastDelegate; if (multiDel != null) { @@ -560,7 +589,10 @@ namespace Crow } } } - internal static Delegate createDel(Type eventType, object instance, string method){ + /// + /// create delegate helper + /// + static Delegate createDel(Type eventType, object instance, string method){ Type t = instance.GetType (); MethodInfo mi = t.GetMethod (method, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); if (mi == null) { @@ -569,7 +601,7 @@ namespace Crow } return Delegate.CreateDelegate (eventType, instance, mi); } - public static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ + public static Delegate compileDynEventHandler2(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ #if DEBUG_BINDING Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); #endif @@ -596,6 +628,130 @@ namespace Crow string [] srcLines = expression.Trim ().Split (new char [] { ';' }); + foreach (string srcLine in srcLines) { + string statement = srcLine.Trim (); + + string [] operandes = statement.Split (new char [] { '=' }); + if (operandes.Length < 2) //not an affectation + { + //maybe we could handle here handler function name + continue; + } + + string rop = operandes [operandes.Length - 1].Trim (); + + #region LEFT OPERANDES + string [] lopParts = operandes [0].Trim ().Split ('.'); + MemberInfo lopMI = null; + + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + + if (lopParts.Length > 1) { + NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts); + CompilerServices.emitGetInstance (il, currentNode, lopNA); + lopType = lopNA.NodeType; + } + + string [] bindTrg = lopParts.Last().Split ('.'); + + if (bindTrg.Length == 1) + lopMI = lopType.GetMember (bindTrg [0]).FirstOrDefault(); + else if (bindTrg.Length == 2) { + //named target + //TODO: + il.Emit(OpCodes.Ldstr, bindTrg[0]); + il.Emit(OpCodes.Callvirt, miFindByName); + lopMI = lopType.GetMember (bindTrg [1]).FirstOrDefault(); + } else + throw new Exception ("Syntax error in binding, expected 'go dot member'"); + + + if (lopMI == null) + throw new Exception (string.Format ("IML BINDING: Member not found")); + + OpCode lopSetOpCode; + dynamic lopSetMI; + Type lopT = null; + switch (lopMI.MemberType) { + case MemberTypes.Property: + lopSetOpCode = OpCodes.Callvirt; + PropertyInfo lopPi = lopMI as PropertyInfo; + lopT = lopPi.PropertyType; + lopSetMI = lopPi.GetSetMethod (); + break; + case MemberTypes.Field: + lopSetOpCode = OpCodes.Stfld; + FieldInfo dstFi = lopMI as FieldInfo; + lopT = dstFi.FieldType; + lopSetMI = dstFi; + break; + default: + throw new Exception (string.Format ("GOML:member type not handle")); + } + #endregion + + #region RIGHT OPERANDES + if (rop.StartsWith ("\'")) { + if (!rop.EndsWith ("\'")) + throw new Exception (string.Format + ("GOML:malformed string constant in handler: {0}", rop)); + string strcst = rop.Substring (1, rop.Length - 2); + + il.Emit (OpCodes.Ldstr, strcst); + + }else if (rop.StartsWith ("this",StringComparison.OrdinalIgnoreCase)){ + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack + } else { + if (lopT.IsEnum) + throw new NotImplementedException (); + + MethodInfo lopParseMi = lopT.GetMethod ("Parse"); + if (lopParseMi == null) + throw new Exception (string.Format + ("GOML:no parse method found in: {0}", lopT.Name)); + il.Emit (OpCodes.Ldstr, rop); + il.Emit (OpCodes.Callvirt, lopParseMi); + il.Emit (OpCodes.Unbox_Any, lopT); + } + + #endregion + + //emit left operand assignment + il.Emit (lopSetOpCode, lopSetMI); + } + + il.Emit (OpCodes.Ret); + + return dm.CreateDelegate (sourceEvent.EventHandlerType); + } + + internal static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ + #if DEBUG_BINDING + Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); + #endif + + Type lopType = null; + + if (currentNode == null) + lopType = sourceEvent.DeclaringType;//TODO:double check if derived class could be returned + else + lopType = currentNode.NodeType; + + #region Retrieve EventHandler parameter type + MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo [] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; + #endregion + + Type [] args = { CompilerServices.TObject, handlerArgsType }; + DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", + typeof(void), + args, true); + ILGenerator il = dm.GetILGenerator (256); + il.Emit (OpCodes.Nop); + + string [] srcLines = expression.Trim ().Split (new char [] { ';' }); + foreach (string srcLine in srcLines) { string statement = srcLine.Trim (); @@ -667,7 +823,7 @@ namespace Crow il.Emit (OpCodes.Ldstr, strcst); - }else if (string.Equals(rop,"this",StringComparison.OrdinalIgnoreCase)){ + }else if (rop.StartsWith ("this",StringComparison.OrdinalIgnoreCase)){ il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack } else { if (lopT.IsEnum) @@ -692,7 +848,10 @@ namespace Crow return dm.CreateDelegate (sourceEvent.EventHandlerType); } - public static string[] splitOnSemiColumnOutsideAccolades (string expression){ + /// + /// Splits expression on semicolon but ignore those between accolades + /// + internal static string[] splitOnSemiColumnOutsideAccolades (string expression){ List exps = new List(); int accCount = 0; int expPtr = 0; @@ -716,10 +875,12 @@ namespace Crow exps.Add(expression); return exps.ToArray (); } + + /// /// Gets the node adress from binding expression splitted with '/' starting at a given node /// - public static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){ + internal static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){ int ptr = sourceAddr.Count - 1; //if exp start with '/' => Graphic tree parsing start at source diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 70334e12..e0e6affc 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -705,7 +705,7 @@ namespace Crow string expression; if (!getDefaultEvent(ei, styling, out expression)) continue; - //TODO:dynEventHandler could be cached somewhere, maybe a style instanciato class holding the styling delegate and bound to it. + //TODO:dynEventHandler could be cached somewhere, maybe a style instanciator class holding the styling delegate and bound to it. foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(expression)) { string trimed = exp.Trim(); if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)){ @@ -718,6 +718,7 @@ namespace Crow il.Emit (OpCodes.Call, CompilerServices.miGetEvent); //push expression as 2nd arg of compile il.Emit (OpCodes.Ldstr, trimed.Substring (1, trimed.Length - 2)); + //push null as 3rd arg, currentNode, not known when instanciing il.Emit (OpCodes.Ldnull); il.Emit (OpCodes.Callvirt, CompilerServices.miCompileDynEventHandler); il.Emit (OpCodes.Castclass, ei.EventHandlerType); diff --git a/src/Instantiator.cs b/src/Instantiator.cs index db0bd353..305df437 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -356,7 +356,7 @@ namespace Crow void readPropertyBinding (Context ctx, string sourceMember, string expression) { NodeAddress sourceNA = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = splitBindingExp (sourceNA, sourceMember, expression); + BindingDefinition bindingDef = genBindingDef (sourceNA, sourceMember, expression); #if DEBUG_BINDING Debug.WriteLine("Property Binding: " + bindingDef.ToString()); @@ -369,15 +369,9 @@ namespace Crow } /// - /// Splits the binding expression + /// get BindingDefinition from binding expression /// - /// true, if it's a two way binding, false otherwise. - /// Source Node address - /// Expression. - /// Target Node Address - /// Target member name - /// Target node name - BindingDefinition splitBindingExp(NodeAddress sourceNA, string sourceMember, string expression){ + BindingDefinition genBindingDef(NodeAddress sourceNA, string sourceMember, string expression){ BindingDefinition bindingDef = new BindingDefinition(sourceNA, sourceMember); if (string.IsNullOrEmpty (expression)) { return bindingDef; @@ -429,7 +423,7 @@ namespace Crow /// Emits handler method bindings void emitHandlerBinding (Context ctx, EventInfo sourceEvent, string expression){ NodeAddress currentNode = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = splitBindingExp (currentNode, sourceEvent.Name, expression); + BindingDefinition bindingDef = genBindingDef (currentNode, sourceEvent.Name, expression); #if DEBUG_BINDING Debug.WriteLine("Event Binding: " + bindingDef.ToString()); @@ -437,7 +431,7 @@ namespace Crow if (bindingDef.IsTemplateBinding | bindingDef.IsDataSourceBinding) { //we need to bind datasource method to source event - DynamicMethod dm = new DynamicMethod ("dyn_dschangedForHandler" + NewId, + DynamicMethod dm = new DynamicMethod ("dyn_dsORtmpChangedForHandler" + NewId, typeof(void), CompilerServices.argsBoundDSChange, true); -- cgit v1.3.1-4-g156e From 3491fb025162f176d10216e2f960cbad42b3bba2 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Fri, 20 Jan 2017 15:37:47 +0100 Subject: binding expression allowing named and unamed target, code clean and comments --- src/CompilerServices/CompilerServices.cs | 191 ++++++++++++++++--------------- src/Instantiator.cs | 23 ++-- 2 files changed, 109 insertions(+), 105 deletions(-) (limited to 'src/CompilerServices') diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 9ddf9991..86dbd66a 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -237,71 +237,6 @@ namespace Crow #endregion #region Reflexion helpers - /// - /// Gets all extension methods defined in assembly for Type - /// - /// Extension methods enumerable - /// Assembly - /// Extended type to search for - internal static IEnumerable GetExtensionMethods (Assembly assembly, - Type extendedType) - { - IEnumerable query = null; - Type curType = extendedType; - - do { - query = from type in assembly.GetTypes () - where type.IsSealed && !type.IsGenericType && !type.IsNested - from method in type.GetMethods (BindingFlags.Static - | BindingFlags.Public | BindingFlags.NonPublic) - where method.IsDefined (typeof (ExtensionAttribute), false) - where method.GetParameters () [0].ParameterType == curType - select method; - - if (query.Count () > 0) - break; - - curType = curType.BaseType; - } while (curType != null); - - return query; - } - /// - /// search for extentions method in entry assembly then in crow assembly - /// - /// Extention MethodInfo - /// Extended type - /// Extention method name - internal static MethodInfo SearchExtMethod(Type t, string methodName){ - MethodInfo mi = null; - mi = GetExtensionMethods (Assembly.GetEntryAssembly(), t) - .Where (em => em.Name == methodName).FirstOrDefault (); - if (mi != null) - return mi; - - return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) - .Where (em => em.Name == methodName).FirstOrDefault (); - } - /// - /// retrieve event handler in class or ancestors - /// - static FieldInfo getEventHandlerField (Type type, string eventName) - { - FieldInfo fi; - Type ty = type; - do { - fi = ty.GetField (eventName, - BindingFlags.NonPublic | - BindingFlags.Instance | - BindingFlags.GetField); - ty = ty.BaseType; - if (ty == null) - break; - } while (fi == null); - return fi; - } - - static MemberInfo getMemberInfoWithReflexion(object instance, string member){ return instance.GetType ().GetMember (member)?.FirstOrDefault(); } @@ -374,6 +309,87 @@ namespace Crow return null; } + /// + /// Gets all extension methods defined in assembly for Type + /// + /// Extension methods enumerable + /// Assembly + /// Extended type to search for + internal static IEnumerable GetExtensionMethods (Assembly assembly, + Type extendedType) + { + IEnumerable query = null; + Type curType = extendedType; + + do { + query = from type in assembly.GetTypes () + where type.IsSealed && !type.IsGenericType && !type.IsNested + from method in type.GetMethods (BindingFlags.Static + | BindingFlags.Public | BindingFlags.NonPublic) + where method.IsDefined (typeof (ExtensionAttribute), false) + where method.GetParameters () [0].ParameterType == curType + select method; + + if (query.Count () > 0) + break; + + curType = curType.BaseType; + } while (curType != null); + + return query; + } + /// + /// search for extentions method in entry assembly then in crow assembly + /// + /// Extention MethodInfo + /// Extended type + /// Extention method name + internal static MethodInfo SearchExtMethod(Type t, string methodName){ + MethodInfo mi = null; + mi = GetExtensionMethods (Assembly.GetEntryAssembly(), t) + .Where (em => em.Name == methodName).FirstOrDefault (); + if (mi != null) + return mi; + + return GetExtensionMethods (Assembly.GetExecutingAssembly(), t) + .Where (em => em.Name == methodName).FirstOrDefault (); + } + /// + /// retrieve event handler in class or ancestors + /// + static FieldInfo getEventHandlerField (Type type, string eventName) + { + FieldInfo fi; + Type ty = type; + do { + fi = ty.GetField (eventName, + BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.GetField); + ty = ty.BaseType; + if (ty == null) + break; + } while (fi == null); + return fi; + } + /// + /// search for an implicit conversion method in origine or destination classes + /// + static MethodInfo getImplicitOp(Type origType, Type dstType){ + foreach(MethodInfo mi in origType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ + if (mi.Name == "op_Implicit") { + if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) + return mi; + } + } + foreach(MethodInfo mi in dstType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ + if (mi.Name == "op_Implicit") { + if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) + return mi; + } + } + return null; + } #endregion /// @@ -432,6 +448,7 @@ namespace Crow return; } } + /// /// Emit MSIL for conversion from orig type to dest type /// @@ -529,24 +546,7 @@ namespace Crow il.MarkLabel (endConvert); } - /// - /// search for an implicit conversion method in origine or destination classes - /// - static MethodInfo getImplicitOp(Type origType, Type dstType){ - foreach(MethodInfo mi in origType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ - if (mi.Name == "op_Implicit") { - if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) - return mi; - } - } - foreach(MethodInfo mi in dstType.GetMethods(BindingFlags.Public|BindingFlags.Static)){ - if (mi.Name == "op_Implicit") { - if (mi.ReturnType == dstType && mi.GetParameters ().FirstOrDefault ().ParameterType == origType) - return mi; - } - } - return null; - } + /// /// Removes delegate from event handler by name /// @@ -601,6 +601,7 @@ namespace Crow } return Delegate.CreateDelegate (eventType, instance, mi); } + public static Delegate compileDynEventHandler2(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ #if DEBUG_BINDING Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); @@ -647,7 +648,7 @@ namespace Crow il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node if (lopParts.Length > 1) { - NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts); + NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, ref operandes [0]); CompilerServices.emitGetInstance (il, currentNode, lopNA); lopType = lopNA.NodeType; } @@ -771,7 +772,7 @@ namespace Crow il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack if (lopParts.Length > 1) { - NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, lopParts); + NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, ref operandes [0]); CompilerServices.emitGetInstance (il, currentNode, lopNA); lopType = lopNA.NodeType; } @@ -876,29 +877,31 @@ namespace Crow return exps.ToArray (); } - /// - /// Gets the node adress from binding expression splitted with '/' starting at a given node + /// Gets the node adress from binding expression starting at sourceAddr + /// and return in expression remaining part /// - internal static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, string[] bindingExp){ + internal static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, ref string expression){ int ptr = sourceAddr.Count - 1; + string[] splitedExp = expression.Split ('/'); + + if (splitedExp.Length < 2)//dataSource binding + return null; - //if exp start with '/' => Graphic tree parsing start at source - if (string.IsNullOrEmpty (bindingExp [0])) { - //TODO: - } else if (bindingExp [0] == ".") { //search template root + if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//search template root ptr--; while (ptr >= 0) { if (typeof(TemplatedControl).IsAssignableFrom (sourceAddr [ptr].CrowType)) break; ptr--; } - } else if (bindingExp [0] == "..") { //search starting at current node - int levelUp = bindingExp.Length - 1; + } else if (splitedExp [0] == "..") { //search starting at current node + int levelUp = splitedExp.Length - 1; if (levelUp > ptr + 1) throw new Exception ("Binding error: try to bind outside IML source"); ptr -= levelUp; } + expression = splitedExp [splitedExp.Length - 1]; //TODO:change Template special address identified with Nodecount = 0 to something not using array count to 0, //here linq is working without limits checking in compile option //but defining a 0 capacity array with limits cheking enabled, cause 'out of memory' error diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 305df437..883f3129 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -87,7 +87,7 @@ namespace Crow } #endregion - public GraphicObject CreateInstance(Interface iface){ + public GraphicObject CreateInstance(Interface iface){ return loader (iface) as GraphicObject; } @@ -380,21 +380,22 @@ namespace Crow bindingDef.TwoWay = true; expression = expression.Substring (1); } - string[] bindingExp = expression.Split ('/'); - if (bindingExp.Length > 1) - bindingDef.TargetNA = CompilerServices.getNodeAdressFromBindingExp (sourceNA, bindingExp); + string exp = expression; + bindingDef.TargetNA = CompilerServices.getNodeAdressFromBindingExp (sourceNA, ref exp); - string [] bindTrg = bindingExp.Last().Split ('.'); + string [] bindTrg = exp.Split ('.'); + if (bindTrg.Length == 0) + throw new Exception ("invalid binding expression: " + expression); if (bindTrg.Length == 1) bindingDef.TargetMember = bindTrg [0]; - else if (bindTrg.Length == 2) { - //named target - bindingDef.TargetName = bindTrg[0]; - bindingDef.TargetMember = bindTrg [1]; - } else - throw new Exception ("Syntax error in binding, expected 'go dot member'"); + else { + if (!string.IsNullOrEmpty(bindTrg[0]))//searchByName + bindingDef.TargetName = bindTrg[0]; + + bindingDef.TargetMember = exp.Substring (bindTrg[0].Length + 1); + } } return bindingDef; -- cgit v1.3.1-4-g156e From bf810209745cc2f90bb5f60164efaa1b6259a31f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Fri, 20 Jan 2017 16:27:38 +0100 Subject: code clean and comments --- src/CompilerServices/CompilerServices.cs | 38 +--------------- src/IML/Node.cs | 12 ++--- src/IML/NodeAddress.cs | 76 ++++++++++++++++++++++++++++++-- src/Instantiator.cs | 37 +--------------- 4 files changed, 84 insertions(+), 79 deletions(-) (limited to 'src/CompilerServices') diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 86dbd66a..c626a6e2 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -648,7 +648,7 @@ namespace Crow il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node if (lopParts.Length > 1) { - NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, ref operandes [0]); + NodeAddress lopNA = currentNode.ResolveExpression (ref operandes [0]); CompilerServices.emitGetInstance (il, currentNode, lopNA); lopType = lopNA.NodeType; } @@ -772,7 +772,7 @@ namespace Crow il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack if (lopParts.Length > 1) { - NodeAddress lopNA = getNodeAdressFromBindingExp (currentNode, ref operandes [0]); + NodeAddress lopNA = currentNode.ResolveExpression (ref operandes [0]); CompilerServices.emitGetInstance (il, currentNode, lopNA); lopType = lopNA.NodeType; } @@ -876,40 +876,6 @@ namespace Crow exps.Add(expression); return exps.ToArray (); } - - /// - /// Gets the node adress from binding expression starting at sourceAddr - /// and return in expression remaining part - /// - internal static NodeAddress getNodeAdressFromBindingExp(NodeAddress sourceAddr, ref string expression){ - int ptr = sourceAddr.Count - 1; - string[] splitedExp = expression.Split ('/'); - - if (splitedExp.Length < 2)//dataSource binding - return null; - - if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//search template root - ptr--; - while (ptr >= 0) { - if (typeof(TemplatedControl).IsAssignableFrom (sourceAddr [ptr].CrowType)) - break; - ptr--; - } - } else if (splitedExp [0] == "..") { //search starting at current node - int levelUp = splitedExp.Length - 1; - if (levelUp > ptr + 1) - throw new Exception ("Binding error: try to bind outside IML source"); - ptr -= levelUp; - } - expression = splitedExp [splitedExp.Length - 1]; - //TODO:change Template special address identified with Nodecount = 0 to something not using array count to 0, - //here linq is working without limits checking in compile option - //but defining a 0 capacity array with limits cheking enabled, cause 'out of memory' error - return new NodeAddress (sourceAddr.Take(ptr+1).ToArray());//[ptr+1]; - //Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1); - //return new NodeAddress (targetNode); - } - } } diff --git a/src/IML/Node.cs b/src/IML/Node.cs index 4ba61c03..29f204f5 100644 --- a/src/IML/Node.cs +++ b/src/IML/Node.cs @@ -30,16 +30,18 @@ namespace Crow.IML /// public struct Node { - /// Current node type - public Type CrowType; - /// Index in parent, -1 for template - public int Index; - + #region CTOR public Node (Type crowType, int _index = 0) { CrowType = crowType; Index = _index; } + #endregion + + /// Current node type + public Type CrowType; + /// Index in parent, -1 for template + public int Index; public MethodInfo GetAddMethod(int childIdx){ if (typeof (Group).IsAssignableFrom (CrowType)) diff --git a/src/IML/NodeAddress.cs b/src/IML/NodeAddress.cs index 460998da..c81a7f66 100644 --- a/src/IML/NodeAddress.cs +++ b/src/IML/NodeAddress.cs @@ -26,13 +26,83 @@ using System.Reflection.Emit; namespace Crow.IML { + /// + /// Node address is a list of nodes from root to leaf defining a unique node + /// public class NodeAddress : List { - public NodeAddress (Node[] nodes) : base(nodes) { - } + #region CTOR + public NodeAddress (Node[] nodes) : base(nodes) {} + #endregion public Type NodeType { get { return Count == 0 ? null : this[this.Count -1].CrowType; }} + /// + /// Gets the node adress from binding expression starting from this node + /// and return in expression remaining part + /// + public NodeAddress ResolveExpression (ref string expression){ + int ptr = this.Count - 1; + string[] splitedExp = expression.Split ('/'); + + if (splitedExp.Length < 2)//dataSource binding + return null; + + if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//search template root + ptr--; + while (ptr >= 0) { + if (typeof(TemplatedControl).IsAssignableFrom (this [ptr].CrowType)) + break; + ptr--; + } + } else if (splitedExp [0] == "..") { //search starting at current node + int levelUp = splitedExp.Length - 1; + if (levelUp > ptr + 1) + throw new Exception ("Binding error: try to bind outside IML source"); + ptr -= levelUp; + } + expression = splitedExp [splitedExp.Length - 1]; + //TODO:change Template special address identified with Nodecount = 0 to something not using array count to 0, + //here linq is working without limits checking in compile option + //but defining a 0 capacity array with limits cheking enabled, cause 'out of memory' error + return new NodeAddress (this.Take(ptr+1).ToArray());//[ptr+1]; + //Array.Copy (sourceAddr.ToArray (), targetNode, ptr + 1); + //return new NodeAddress (targetNode); + } + /// + /// get BindingDefinition from binding expression + /// + public BindingDefinition GetBindingDef(string sourceMember, string expression){ + BindingDefinition bindingDef = new BindingDefinition(this, sourceMember); + if (string.IsNullOrEmpty (expression)) { + return bindingDef; + } else { + if (expression.StartsWith ("²")) { + bindingDef.TwoWay = true; + expression = expression.Substring (1); + } + + string exp = expression; + bindingDef.TargetNA = this.ResolveExpression (ref exp); + + string [] bindTrg = exp.Split ('.'); + + if (bindTrg.Length == 0) + throw new Exception ("invalid binding expression: " + expression); + if (bindTrg.Length == 1) + bindingDef.TargetMember = bindTrg [0]; + else { + if (!string.IsNullOrEmpty(bindTrg[0]))//searchByName + bindingDef.TargetName = bindTrg[0]; + + bindingDef.TargetMember = exp.Substring (bindTrg[0].Length + 1); + } + } + + return bindingDef; + } + + #region Object overrides public override bool Equals (object obj) { if (obj == null) @@ -50,7 +120,6 @@ namespace Crow.IML return hash; } } - public override string ToString () { string tmp = ""; @@ -58,6 +127,7 @@ namespace Crow.IML tmp += string.Format ("{0};", n.Index); return tmp; } + #endregion } public class NamedNodeAddress : NodeAddress { diff --git a/src/Instantiator.cs b/src/Instantiator.cs index 883f3129..6e94ae17 100644 --- a/src/Instantiator.cs +++ b/src/Instantiator.cs @@ -356,7 +356,7 @@ namespace Crow void readPropertyBinding (Context ctx, string sourceMember, string expression) { NodeAddress sourceNA = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = genBindingDef (sourceNA, sourceMember, expression); + BindingDefinition bindingDef = sourceNA.GetBindingDef (sourceMember, expression); #if DEBUG_BINDING Debug.WriteLine("Property Binding: " + bindingDef.ToString()); @@ -368,39 +368,6 @@ namespace Crow ctx.StorePropertyBinding (bindingDef); } - /// - /// get BindingDefinition from binding expression - /// - BindingDefinition genBindingDef(NodeAddress sourceNA, string sourceMember, string expression){ - BindingDefinition bindingDef = new BindingDefinition(sourceNA, sourceMember); - if (string.IsNullOrEmpty (expression)) { - return bindingDef; - } else { - if (expression.StartsWith ("²")) { - bindingDef.TwoWay = true; - expression = expression.Substring (1); - } - - string exp = expression; - bindingDef.TargetNA = CompilerServices.getNodeAdressFromBindingExp (sourceNA, ref exp); - - string [] bindTrg = exp.Split ('.'); - - if (bindTrg.Length == 0) - throw new Exception ("invalid binding expression: " + expression); - if (bindTrg.Length == 1) - bindingDef.TargetMember = bindTrg [0]; - else { - if (!string.IsNullOrEmpty(bindTrg[0]))//searchByName - bindingDef.TargetName = bindTrg[0]; - - bindingDef.TargetMember = exp.Substring (bindTrg[0].Length + 1); - } - } - - return bindingDef; - } - #region Emit Helper void dataSourceChangedEmitHelper(object dscSource, object dataSource, int dynMethIdx){ if (dataSource is IValueChange) @@ -424,7 +391,7 @@ namespace Crow /// Emits handler method bindings void emitHandlerBinding (Context ctx, EventInfo sourceEvent, string expression){ NodeAddress currentNode = ctx.CurrentNodeAddress; - BindingDefinition bindingDef = genBindingDef (currentNode, sourceEvent.Name, expression); + BindingDefinition bindingDef = currentNode.GetBindingDef (sourceEvent.Name, expression); #if DEBUG_BINDING Debug.WriteLine("Event Binding: " + bindingDef.ToString()); -- cgit v1.3.1-4-g156e From fb73ce236b57ee5a58f498c3c4236e003436d921 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Mon, 23 Jan 2017 18:12:07 +0100 Subject: new CompileDynEventHandler method with new 'BindingMember' class --- Crow.csproj | 1 + src/CompilerServices/CompilerServices.cs | 259 ++++++++----------------------- src/IML/BindingMember.cs | 152 ++++++++++++++++++ 3 files changed, 218 insertions(+), 194 deletions(-) create mode 100644 src/IML/BindingMember.cs (limited to 'src/CompilerServices') diff --git a/Crow.csproj b/Crow.csproj index d4076290..709c7c41 100644 --- a/Crow.csproj +++ b/Crow.csproj @@ -152,6 +152,7 @@ + diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index c626a6e2..dc2f7a1c 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -42,6 +42,9 @@ namespace Crow internal static MethodInfo miCreateDel = typeof(CompilerServices).GetMethod ("createDel", BindingFlags.Static | BindingFlags.NonPublic); internal static MethodInfo miGetImplOp = typeof(CompilerServices).GetMethod ("getImplicitOp", BindingFlags.Static | BindingFlags.NonPublic); + + internal static MethodInfo miGoUpLevels = typeof(CompilerServices).GetMethod("goUpNbLevels", BindingFlags.Static | BindingFlags.NonPublic); + internal static FieldInfo fiCachedDel = typeof(Instantiator).GetField("cachedDelegates", BindingFlags.Instance | BindingFlags.NonPublic); internal static FieldInfo fiTemplateBinding = typeof(Instantiator).GetField("templateBinding", BindingFlags.Instance | BindingFlags.NonPublic); internal static MethodInfo miDSChangeEmitHelper = typeof(Instantiator).GetMethod("dataSourceChangedEmitHelper", BindingFlags.Instance | BindingFlags.NonPublic); @@ -602,7 +605,7 @@ namespace Crow return Delegate.CreateDelegate (eventType, instance, mi); } - public static Delegate compileDynEventHandler2(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ + internal static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ #if DEBUG_BINDING Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); #endif @@ -630,95 +633,71 @@ namespace Crow string [] srcLines = expression.Trim ().Split (new char [] { ';' }); foreach (string srcLine in srcLines) { - string statement = srcLine.Trim (); - - string [] operandes = statement.Split (new char [] { '=' }); - if (operandes.Length < 2) //not an affectation - { - //maybe we could handle here handler function name + if (string.IsNullOrEmpty (srcLine)) continue; - } + string [] operandes = srcLine.Trim ().Split (new char [] { '=' }); + if (operandes.Length != 2) //not an affectation + throw new NotSupportedException (); - string rop = operandes [operandes.Length - 1].Trim (); + System.Reflection.Emit.Label cancel = il.DefineLabel (); + System.Reflection.Emit.Label cancelFinalSet = il.DefineLabel (); + System.Reflection.Emit.Label success = il.DefineLabel (); - #region LEFT OPERANDES - string [] lopParts = operandes [0].Trim ().Split ('.'); - MemberInfo lopMI = null; + BindingMember lop = new BindingMember (operandes [0].Trim ()); + BindingMember rop = new BindingMember (operandes [1].Trim ()); il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node - if (lopParts.Length > 1) { - NodeAddress lopNA = currentNode.ResolveExpression (ref operandes [0]); - CompilerServices.emitGetInstance (il, currentNode, lopNA); - lopType = lopNA.NodeType; - } - - string [] bindTrg = lopParts.Last().Split ('.'); - - if (bindTrg.Length == 1) - lopMI = lopType.GetMember (bindTrg [0]).FirstOrDefault(); - else if (bindTrg.Length == 2) { - //named target - //TODO: - il.Emit(OpCodes.Ldstr, bindTrg[0]); - il.Emit(OpCodes.Callvirt, miFindByName); - lopMI = lopType.GetMember (bindTrg [1]).FirstOrDefault(); - } else - throw new Exception ("Syntax error in binding, expected 'go dot member'"); - - - if (lopMI == null) - throw new Exception (string.Format ("IML BINDING: Member not found")); + #region Left operande + PropertyInfo lopPI = null; - OpCode lopSetOpCode; - dynamic lopSetMI; - Type lopT = null; - switch (lopMI.MemberType) { - case MemberTypes.Property: - lopSetOpCode = OpCodes.Callvirt; - PropertyInfo lopPi = lopMI as PropertyInfo; - lopT = lopPi.PropertyType; - lopSetMI = lopPi.GetSetMethod (); - break; - case MemberTypes.Field: - lopSetOpCode = OpCodes.Stfld; - FieldInfo dstFi = lopMI as FieldInfo; - lopT = dstFi.FieldType; - lopSetMI = dstFi; - break; - default: - throw new Exception (string.Format ("GOML:member type not handle")); - } + //in dyn handler, no datasource binding, so single name in expression are also handled as current node property + if (lop.IsSingleName) + lopPI = lopType.GetProperty (lop.Tokens [0]); + else if (lop.IsCurrentNodeProperty) + lopPI = lopType.GetProperty (lop.Tokens [1]); + else + lop.emitGetTarget (il, cancel); #endregion #region RIGHT OPERANDES - if (rop.StartsWith ("\'")) { - if (!rop.EndsWith ("\'")) - throw new Exception (string.Format - ("GOML:malformed string constant in handler: {0}", rop)); - string strcst = rop.Substring (1, rop.Length - 2); - - il.Emit (OpCodes.Ldstr, strcst); - - }else if (rop.StartsWith ("this",StringComparison.OrdinalIgnoreCase)){ - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack - } else { - if (lopT.IsEnum) - throw new NotImplementedException (); + if (rop.IsStringConstant){ + il.Emit (OpCodes.Ldstr, rop.Tokens[0]); + lop.emitSetProperty (il, cancelFinalSet); + }else if (rop.LevelsUp ==0 && !string.IsNullOrEmpty(rop.Tokens[0])) {//parsable constant depending on lop type + //if left operand is member of current node, it's easy to fetch type, else we should use reflexion in msil + if (lopPI == null){//accept GraphicObj members, but it's restricive + //TODO: we should get the parse method by reflexion, or something else + lopPI = typeof(GraphicObject).GetProperty (lop.Tokens [lop.Tokens.Length-1]); + if (lopPI == null) + throw new NotSupportedException (); + } - MethodInfo lopParseMi = lopT.GetMethod ("Parse"); + MethodInfo lopParseMi = lopPI.PropertyType.GetMethod ("Parse"); if (lopParseMi == null) throw new Exception (string.Format - ("GOML:no parse method found in: {0}", lopT.Name)); - il.Emit (OpCodes.Ldstr, rop); + ("IML: no static 'Parse' method found in: {0}", lopPI.PropertyType.Name)); + + il.Emit (OpCodes.Ldstr, operandes [1].Trim ()); il.Emit (OpCodes.Callvirt, lopParseMi); - il.Emit (OpCodes.Unbox_Any, lopT); + il.Emit (OpCodes.Unbox_Any, lopPI.PropertyType); + + //emit left operand assignment + il.Emit (OpCodes.Callvirt, lopPI.GetSetMethod()); + } else {//tree parsing and propert gets + rop.emitGetTarget (il, cancel); + rop.emitGetProperty (il, cancel); + lop.emitSetProperty (il, cancelFinalSet); } - #endregion - //emit left operand assignment - il.Emit (lopSetOpCode, lopSetMI); + il.Emit (OpCodes.Br, success); + + il.MarkLabel (cancelFinalSet); + il.Emit (OpCodes.Pop); //pop null MemberInfo on the stack causing cancelation + il.MarkLabel (cancel); + il.Emit (OpCodes.Pop); //pop null instance on the stack causing cancelation + il.MarkLabel (success); } il.Emit (OpCodes.Ret); @@ -726,128 +705,20 @@ namespace Crow return dm.CreateDelegate (sourceEvent.EventHandlerType); } - internal static Delegate compileDynEventHandler(EventInfo sourceEvent, string expression, NodeAddress currentNode = null){ - #if DEBUG_BINDING - Debug.WriteLine ("\tCompile Event {0}: {1}", sourceEvent.Name, expression); - #endif - - Type lopType = null; - - if (currentNode == null) - lopType = sourceEvent.DeclaringType;//TODO:double check if derived class could be returned - else - lopType = currentNode.NodeType; - - #region Retrieve EventHandler parameter type - MethodInfo evtInvoke = sourceEvent.EventHandlerType.GetMethod ("Invoke"); - ParameterInfo [] evtParams = evtInvoke.GetParameters (); - Type handlerArgsType = evtParams [1].ParameterType; - #endregion - - Type [] args = { CompilerServices.TObject, handlerArgsType }; - DynamicMethod dm = new DynamicMethod ("dyn_eventHandler", - typeof(void), - args, true); - ILGenerator il = dm.GetILGenerator (256); - il.Emit (OpCodes.Nop); - - string [] srcLines = expression.Trim ().Split (new char [] { ';' }); - - foreach (string srcLine in srcLines) { - string statement = srcLine.Trim (); - - string [] operandes = statement.Split (new char [] { '=' }); - if (operandes.Length < 2) //not an affectation - { - //maybe we could handle here handler function name - continue; - } - - string rop = operandes [operandes.Length - 1].Trim (); - - #region LEFT OPERANDES - string [] lopParts = operandes [0].Trim ().Split ('/'); - MemberInfo lopMI = null; - - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack - - if (lopParts.Length > 1) { - NodeAddress lopNA = currentNode.ResolveExpression (ref operandes [0]); - CompilerServices.emitGetInstance (il, currentNode, lopNA); - lopType = lopNA.NodeType; - } - - string [] bindTrg = lopParts.Last().Split ('.'); - - if (bindTrg.Length == 1) - lopMI = lopType.GetMember (bindTrg [0]).FirstOrDefault(); - else if (bindTrg.Length == 2) { - //named target - //TODO: - il.Emit(OpCodes.Ldstr, bindTrg[0]); - il.Emit(OpCodes.Callvirt, miFindByName); - lopMI = lopType.GetMember (bindTrg [1]).FirstOrDefault(); - } else - throw new Exception ("Syntax error in binding, expected 'go dot member'"); - - - if (lopMI == null) - throw new Exception (string.Format ("IML BINDING: Member not found")); - - OpCode lopSetOpCode; - dynamic lopSetMI; - Type lopT = null; - switch (lopMI.MemberType) { - case MemberTypes.Property: - lopSetOpCode = OpCodes.Callvirt; - PropertyInfo lopPi = lopMI as PropertyInfo; - lopT = lopPi.PropertyType; - lopSetMI = lopPi.GetSetMethod (); - break; - case MemberTypes.Field: - lopSetOpCode = OpCodes.Stfld; - FieldInfo dstFi = lopMI as FieldInfo; - lopT = dstFi.FieldType; - lopSetMI = dstFi; - break; - default: - throw new Exception (string.Format ("GOML:member type not handle")); - } - #endregion - - #region RIGHT OPERANDES - if (rop.StartsWith ("\'")) { - if (!rop.EndsWith ("\'")) - throw new Exception (string.Format - ("GOML:malformed string constant in handler: {0}", rop)); - string strcst = rop.Substring (1, rop.Length - 2); - - il.Emit (OpCodes.Ldstr, strcst); - - }else if (rop.StartsWith ("this",StringComparison.OrdinalIgnoreCase)){ - il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack - } else { - if (lopT.IsEnum) - throw new NotImplementedException (); - - MethodInfo lopParseMi = lopT.GetMethod ("Parse"); - if (lopParseMi == null) - throw new Exception (string.Format - ("GOML:no parse method found in: {0}", lopT.Name)); - il.Emit (OpCodes.Ldstr, rop); - il.Emit (OpCodes.Callvirt, lopParseMi); - il.Emit (OpCodes.Unbox_Any, lopT); - } - - #endregion - - //emit left operand assignment - il.Emit (lopSetOpCode, lopSetMI); + /// + /// MSIL helper, go n levels up + /// + /// true, if logical parents are not null + /// Start Instance + /// Levels to go upward + internal static ILayoutable goUpNbLevels(ILayoutable instance, int levelCount){ + ILayoutable tmp = instance; + int i = 0; + while (tmp != null && i < levelCount) { + tmp = tmp.LogicalParent; + i++; } - - il.Emit (OpCodes.Ret); - - return dm.CreateDelegate (sourceEvent.EventHandlerType); + return tmp; } /// /// Splits expression on semicolon but ignore those between accolades diff --git a/src/IML/BindingMember.cs b/src/IML/BindingMember.cs new file mode 100644 index 00000000..1bab5959 --- /dev/null +++ b/src/IML/BindingMember.cs @@ -0,0 +1,152 @@ +// +// BindingMember.cs +// +// Author: +// Jean-Philippe Bruyère +// +// Copyright (c) 2017 jp +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +using System; +using System.Reflection.Emit; + +namespace Crow +{ + /// + /// Expression token, a variable, a string constant or a parsable constant (having a static Parse method) + /// '../' => 1 level up in graphic tree + /// './' or '/' => template root level + /// '.Name1.Name2' current level properties + /// 'name.prop' named descendant in graphic tree, search with 'FindByName' method of GraphicObject + /// + public class BindingMember + { + /// + /// true if expression was enclosed in ' + /// + public bool IsStringConstant = false; + /// + /// Nb level to go up, '-1' for template root + /// + public int LevelsUp; + /// + /// Remaining string after '/' split, splitted on '.' + /// + public string[] Tokens; + + /// + /// Target the template's root node, expression was in the form './name[.name[...]]' or '/name[.name[...]]' + /// + public bool IsTemplateBinding { get { return LevelsUp < 0; }} + + /// + /// No level change and expression was '.name' + /// + /// true if this instance is current node property; otherwise, false. + public bool IsCurrentNodeProperty { + get { + return LevelsUp == 0 && ( Tokens.Length == 2 && string.IsNullOrEmpty(Tokens[0])); + } + } + /// + /// no level change, and only a single name in Tokens[], that's dataSource member if property binding + /// + public bool IsSingleName { get { return LevelsUp == 0 && Tokens.Length == 1; }} + + #region CTOR + public BindingMember (){} + public BindingMember (string expression){ + if (string.IsNullOrEmpty (expression)) + return; + + string[] splitedExp = expression.Trim().Split ('/'); + + int ptr = 0; + if (splitedExp.Length == 1) { + if (splitedExp [0].StartsWith ("\'")) { + if (!splitedExp [0].EndsWith ("\'")) + throw new Exception (string.Format + ("IML:malformed string constant in binding expression: {0}", splitedExp [0])); + Tokens = new string[] { splitedExp [0].Substring (1, splitedExp [0].Length - 2) }; + IsStringConstant = true; + return; + } + } else { + if (string.IsNullOrEmpty (splitedExp [0]) || splitedExp [0] == ".") {//template root + LevelsUp = -1; + ptr++; + } else { + while (splitedExp [ptr] == "..") + ptr++; + } + } + if (ptr != splitedExp.Length - 1) + throw new Exception ("invalid expresion: " + expression); + Tokens = splitedExp [ptr].Split ('.'); + } + #endregion + + public void emitGetTarget(ILGenerator il, System.Reflection.Emit.Label cancel){ + + if (IsTemplateBinding) { + System.Reflection.Emit.Label nextLogicParent = il.DefineLabel (); + il.MarkLabel (nextLogicParent); + il.Emit (OpCodes.Callvirt, CompilerServices.miGetLogicalParent); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Isinst, typeof(TemplatedControl)); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, nextLogicParent); + } else if (LevelsUp > 0) {//go upward in logical tree + il.Emit (OpCodes.Ldind_I4, LevelsUp);//push arg 2 of goUpLevels + il.Emit (OpCodes.Callvirt, CompilerServices.miGoUpLevels); + //test if null + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + + if (!string.IsNullOrEmpty (Tokens [0])) {//find by name + il.Emit (OpCodes.Ldstr, Tokens [0]); + il.Emit (OpCodes.Callvirt, CompilerServices.miFindByName); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + + for (int i = 1; i < Tokens.Length -1; i++) { + il.Emit (OpCodes.Ldstr, Tokens [i]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + } + } + public void emitGetProperty(ILGenerator il, System.Reflection.Emit.Label cancel) { + il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); + } + public void emitSetProperty(ILGenerator il, System.Reflection.Emit.Label cancel) { + il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name + il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); + il.Emit (OpCodes.Dup); + il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); + } + } +} + -- cgit v1.3.1-4-g156e From a84e509116b46aa1a98e80460e6a82e2056b9475 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bruyère Date: Mon, 23 Jan 2017 21:59:14 +0100 Subject: debug new dynEvent handler --- src/CompilerServices/CompilerServices.cs | 31 ++++++++++++++++++++----------- src/IML/BindingMember.cs | 22 +++++++++++++++------- 2 files changed, 35 insertions(+), 18 deletions(-) (limited to 'src/CompilerServices') diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index dc2f7a1c..31b29fb2 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -270,13 +270,17 @@ namespace Crow destType =(miDest as FieldInfo).FieldType; if (value != null) { - origType = value.GetType (); - if (destType.IsAssignableFrom (origType)) - convertedVal = Convert.ChangeType (value, destType); - else if (origType.IsPrimitive & destType.IsPrimitive) - convertedVal = GetConvertMethod (destType).Invoke (null, new Object[] { value }); - else - convertedVal = getImplicitOp (origType, destType).Invoke (value, null); + if (destType == TObject)//TODO: check that test of destType is not causing problems + convertedVal = value; + else { + origType = value.GetType (); + if (destType.IsAssignableFrom (origType)) + convertedVal = Convert.ChangeType (value, destType); + else if (origType.IsPrimitive & destType.IsPrimitive) + convertedVal = GetConvertMethod (destType).Invoke (null, new Object[] { value }); + else + convertedVal = getImplicitOp (origType, destType).Invoke (value, null); + } } if (miDest.MemberType == MemberTypes.Property) @@ -663,7 +667,10 @@ namespace Crow #region RIGHT OPERANDES if (rop.IsStringConstant){ il.Emit (OpCodes.Ldstr, rop.Tokens[0]); - lop.emitSetProperty (il, cancelFinalSet); + lop.emitSetProperty (il); + }else if (rop.IsSingleName && rop.Tokens[0] == "this"){ + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + lop.emitSetProperty (il); }else if (rop.LevelsUp ==0 && !string.IsNullOrEmpty(rop.Tokens[0])) {//parsable constant depending on lop type //if left operand is member of current node, it's easy to fetch type, else we should use reflexion in msil if (lopPI == null){//accept GraphicObj members, but it's restricive @@ -685,9 +692,11 @@ namespace Crow //emit left operand assignment il.Emit (OpCodes.Callvirt, lopPI.GetSetMethod()); } else {//tree parsing and propert gets - rop.emitGetTarget (il, cancel); - rop.emitGetProperty (il, cancel); - lop.emitSetProperty (il, cancelFinalSet); + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node + + rop.emitGetTarget (il, cancelFinalSet); + rop.emitGetProperty (il, cancelFinalSet); + lop.emitSetProperty (il); } #endregion diff --git a/src/IML/BindingMember.cs b/src/IML/BindingMember.cs index 1bab5959..f643d10b 100644 --- a/src/IML/BindingMember.cs +++ b/src/IML/BindingMember.cs @@ -89,6 +89,7 @@ namespace Crow } else { while (splitedExp [ptr] == "..") ptr++; + LevelsUp = ptr; } } if (ptr != splitedExp.Length - 1) @@ -109,7 +110,7 @@ namespace Crow il.Emit (OpCodes.Dup); il.Emit (OpCodes.Brfalse, nextLogicParent); } else if (LevelsUp > 0) {//go upward in logical tree - il.Emit (OpCodes.Ldind_I4, LevelsUp);//push arg 2 of goUpLevels + il.Emit (OpCodes.Ldc_I4, LevelsUp);//push arg 2 of goUpLevels il.Emit (OpCodes.Callvirt, CompilerServices.miGoUpLevels); //test if null il.Emit (OpCodes.Dup); @@ -124,27 +125,34 @@ namespace Crow } for (int i = 1; i < Tokens.Length -1; i++) { + System.Reflection.Emit.Label miOK = il.DefineLabel (); + il.Emit (OpCodes.Dup);//duplicate instance il.Emit (OpCodes.Ldstr, Tokens [i]);//load member name il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Brtrue, miOK); + il.Emit (OpCodes.Pop);//pop dup instance + il.Emit (OpCodes.Br, cancel); + il.MarkLabel (miOK); il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); il.Emit (OpCodes.Dup); il.Emit (OpCodes.Brfalse, cancel); } } public void emitGetProperty(ILGenerator il, System.Reflection.Emit.Label cancel) { + System.Reflection.Emit.Label miOK = il.DefineLabel (); + il.Emit (OpCodes.Dup);//duplicate instance il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, cancel); + il.Emit (OpCodes.Brtrue, miOK); + il.Emit (OpCodes.Pop);//pop dup instance + il.Emit (OpCodes.Br, cancel); + il.MarkLabel (miOK); il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); } - public void emitSetProperty(ILGenerator il, System.Reflection.Emit.Label cancel) { + public void emitSetProperty(ILGenerator il) { il.Emit (OpCodes.Ldstr, Tokens [Tokens.Length -1]);//load member name - il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); - il.Emit (OpCodes.Dup); - il.Emit (OpCodes.Brfalse, cancel); il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); } } -- cgit v1.3.1-4-g156e