From: jpbruyere Date: Mon, 12 Oct 2015 07:20:15 +0000 (+0200) Subject: new binding system: DynEvents compilation X-Git-Tag: 0.3~113 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=e3fbc04e878808dd67a5c06fa22cff49d6e54e61;p=jp%2Fcrow.git new binding system: DynEvents compilation --- diff --git a/Templates/Expandable.goml b/Templates/Expandable.goml index 844cacb4..1f3c5281 100755 --- a/Templates/Expandable.goml +++ b/Templates/Expandable.goml @@ -1,5 +1,6 @@  - + diff --git a/Tests/GOLIBTests.cs b/Tests/GOLIBTests.cs index 8ad93184..13576b24 100644 --- a/Tests/GOLIBTests.cs +++ b/Tests/GOLIBTests.cs @@ -35,21 +35,21 @@ namespace test int idx = 0; string[] testFiles = { "fps.goml", + "testCheckbox.goml", + "testExpandable.goml", "testContainer.goml", "testBorder.goml", "testLabel.goml", - "testCheckbox.goml", "testRadioButton.goml", "testSpinner.goml", "testPopper.goml", - "testExpandable.goml", "testGroupBox.goml", - "testCombobox.goml", "testWindow.goml", "testMsgBox.goml", "testGrid.goml", - "test4.goml", "testMeter.goml", +// "testCombobox.goml", +// "test4.goml", }; #region FPS @@ -100,6 +100,7 @@ namespace test { base.OnLoad (e); //this.AddWidget(new test4()); + LoadInterface("Interfaces/" + testFiles[idx]); } protected override void OnUpdateFrame (FrameEventArgs e) diff --git a/Tests/Interfaces/testCheckbox.goml b/Tests/Interfaces/testCheckbox.goml index 7f5078a5..999d79f9 100755 --- a/Tests/Interfaces/testCheckbox.goml +++ b/Tests/Interfaces/testCheckbox.goml @@ -1,3 +1,3 @@  - + diff --git a/Tests/Interfaces/testExpandable.goml b/Tests/Interfaces/testExpandable.goml index 1627055b..5cae7ae6 100755 --- a/Tests/Interfaces/testExpandable.goml +++ b/Tests/Interfaces/testExpandable.goml @@ -5,5 +5,5 @@ - + diff --git a/src/CompilerServices/CompilerServices.cs b/src/CompilerServices/CompilerServices.cs index 44b930e0..0d13d7bf 100644 --- a/src/CompilerServices/CompilerServices.cs +++ b/src/CompilerServices/CompilerServices.cs @@ -17,14 +17,17 @@ namespace go PropertyBinding } public class Binding{ - public PropertyInfo Property; + public MemberInfo SourceMember; public string Expression; + public PropertyInfo Property { get { return SourceMember as PropertyInfo; } } + public EventInfo Event { get { return SourceMember as EventInfo; } } + public Binding(){ } - public Binding(PropertyInfo _property, string _expression) + public Binding(MemberInfo _sourceMember, string _expression) { - Property = _property; + SourceMember = _sourceMember; Expression = _expression; } } @@ -45,12 +48,15 @@ namespace go public bool FindTarget(GraphicObject source){ string member = null; + //if binding exp = '{}' => binding is done on datasource if (string.IsNullOrEmpty (Binding.Expression)) { Target = source; Member = null; return true; } + //if (Binding.SourceMember.MemberType == MemberTypes.Event) + string[] bindingExp = Binding.Expression.Split ('/'); if (bindingExp.Length == 1) { @@ -81,11 +87,14 @@ namespace go string[] bindTrg = bindingExp [ptr].Split ('.'); - if (bindTrg.Length == 2) { + if (bindTrg.Length == 1) + member = bindTrg [0]; + else if (bindTrg.Length == 2){ tmp = (tmp as GraphicObject).FindByName (bindTrg [0]); member = bindTrg [1]; } else throw new Exception ("Syntax error in binding, expected 'go dot member'"); + Target = tmp; } if (Target == null) { @@ -266,14 +275,10 @@ namespace go #endregion Type[] args = {typeof(object), handlerArgsType}; - DynamicMethod dm = new DynamicMethod("dynHandle_" + dynHandleCpt, + DynamicMethod dm = new DynamicMethod("dynHandle", typeof(void), args, srcType.Module); - - es.Source.DynamicMethodIds.Add (dynHandleCpt); - - dynHandleCpt++; #region IL generation ILGenerator il = dm.GetILGenerator(256); @@ -355,7 +360,7 @@ namespace go }else{ //search if parsing methods are present MethodInfo lopTryParseMi = lopT.GetMethod("TryParse"); - + //TODO } } diff --git a/src/GraphicObjects/Expandable.cs b/src/GraphicObjects/Expandable.cs index f9e81c1a..0bc5d067 100644 --- a/src/GraphicObjects/Expandable.cs +++ b/src/GraphicObjects/Expandable.cs @@ -96,7 +96,7 @@ namespace go } onCollapse (this, null); - NotifyValueChanged ("SvgSub", "collapsed"); + NotifyValueChanged ("SvgSub", "collapsed"); } } diff --git a/src/GraphicObjects/GraphicObject.cs b/src/GraphicObjects/GraphicObject.cs index 7aba98c8..7ebcaf61 100644 --- a/src/GraphicObjects/GraphicObject.cs +++ b/src/GraphicObjects/GraphicObject.cs @@ -792,16 +792,36 @@ namespace go { List resolved = new List (); foreach (Binding b in Bindings) { + if (b.SourceMember.MemberType == MemberTypes.Event) { + if (b.Expression.StartsWith("{")){ + CompileEventSource(b); + continue; + } + } ResolvedBinding rb = new ResolvedBinding (b); if (!rb.FindTarget (this)) continue; + if (rb.Binding.SourceMember.MemberType == MemberTypes.Event) { + //register handler for event + MethodInfo mi = rb.Member as MethodInfo; + if (mi == null) { + Debug.WriteLine ("Handler Method not found: " + rb.Binding.Expression); + continue; + } + + MethodInfo addHandler = rb.Binding.Event.GetAddMethod (); + Delegate del = Delegate.CreateDelegate (rb.Binding.Event.EventHandlerType, rb.Target, mi); + addHandler.Invoke (this, new object[] { del }); + continue; + } resolved.Add (rb); } + //group;only one dynMethods by target (valuechanged event source) + //changed value name tested in switch IEnumerable groupedByTarget = resolved.GroupBy (g => g.Target, g => g, (k, g) => g.ToArray ()); foreach (ResolvedBinding[] grouped in groupedByTarget) { int i = 0; - object targetValue = grouped [0].Target;//empty binding exp=> bound to target object by default - Type targetType = targetValue.GetType(); + Type targetType = grouped[0].Target.GetType(); Type sourceType = this.GetType(); DynamicMethod dm = null; @@ -839,8 +859,8 @@ namespace go jumpTable = new System.Reflection.Emit.Label[grouped.Length]; for (i = 0; i < grouped.Length; i++) jumpTable [i] = il.DefineLabel (); - lbMemberName = il.DeclareLocal(typeof(string)); - lbValue = il.DeclareLocal(typeof(object)); + il.DeclareLocal(typeof(string)); + il.DeclareLocal(typeof(object)); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); @@ -849,13 +869,13 @@ namespace go il.Emit(OpCodes.Ldarg_2); FieldInfo fiNewValue = typeof(ValueChangeEventArgs).GetField("NewValue"); il.Emit(OpCodes.Ldfld, fiNewValue); - il.Emit(OpCodes.Stloc, lbValue); + il.Emit(OpCodes.Stloc_1); //push name il.Emit(OpCodes.Ldarg_2); FieldInfo fiMbName = typeof(ValueChangeEventArgs).GetField("MemberName"); il.Emit(OpCodes.Ldfld, fiMbName); - il.Emit(OpCodes.Stloc, lbMemberName); - il.Emit(OpCodes.Ldloc, lbMemberName); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Brfalse, endMethod); } #endregion @@ -863,6 +883,7 @@ namespace go i = 0; foreach (ResolvedBinding rb in grouped) { #region initialize target with actual value + object targetValue = null; if (rb.Member != null){ if (rb.Member.MemberType == MemberTypes.Property) targetValue = (rb.Member as PropertyInfo).GetGetMethod ().Invoke (rb.Target, null); @@ -876,10 +897,14 @@ namespace go targetValue = mthSrc.Invoke(rb.Target, null); }else throw new Exception ("unandled source member type for binding"); - } + }else if (string.IsNullOrEmpty(rb.Binding.Expression)) + targetValue= grouped [0].Target;//empty binding exp=> bound to target object by default //TODO: handle other dest type conversions if (rb.Binding.Property.PropertyType == typeof(string)){ - if (targetValue != null) + if (targetValue == null){ + //set default value + + }else targetValue = targetValue.ToString (); } rb.Binding.Property.GetSetMethod ().Invoke (this, new object[] { targetValue }); @@ -889,8 +914,11 @@ namespace go if (il == null) continue; - il.Emit (OpCodes.Ldloc, lbMemberName); - il.Emit (OpCodes.Ldstr, rb.Member.Name); + il.Emit (OpCodes.Ldloc_0); + if (rb.Member != null) + il.Emit (OpCodes.Ldstr, rb.Member.Name); + else + il.Emit (OpCodes.Ldstr, rb.Binding.Expression); il.Emit (OpCodes.Callvirt, stringEquals); il.Emit (OpCodes.Brtrue, jumpTable[i]); i++; @@ -905,7 +933,7 @@ namespace go foreach (ResolvedBinding rb in grouped) { il.MarkLabel (jumpTable [i]); - il.Emit(OpCodes.Ldloc, lbValue); + il.Emit(OpCodes.Ldloc_1); //by default, target value type is deducted from source member type to allow //memberless binding, if targetMember exists, it will be used to determine target @@ -941,6 +969,125 @@ namespace go addHandler.Invoke(grouped [0].Target, new object[] {del}); } } + /// + /// Compile events expression in GOML attributes + /// + /// Event binding details + public void CompileEventSource(Binding binding) + { + Type sourceType = this.GetType(); + + #region Retrieve EventHandler parameter type + MethodInfo evtInvoke = binding.Event.EventHandlerType.GetMethod ("Invoke"); + ParameterInfo[] evtParams = evtInvoke.GetParameters (); + Type handlerArgsType = evtParams [1].ParameterType; + #endregion + + Type[] args = {typeof(object), typeof(object),handlerArgsType}; + DynamicMethod dm = new DynamicMethod("dynHandle", + typeof(void), + args, + sourceType); + + + #region IL generation + ILGenerator il = dm.GetILGenerator(256); + + string src = binding.Expression.Trim(); + + if (! (src.StartsWith("{") || src.EndsWith ("}"))) + throw new Exception (string.Format("GOML:Malformed {0} Event handler: {1}", binding.SourceMember.Name, binding.Expression)); + + src = src.Substring (1, src.Length - 2); + string[] srcLines = src.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 + { + continue; + } + string lop = operandes [0].Trim (); + string rop = operandes [operandes.Length-1].Trim (); + + #region LEFT OPERANDES + GraphicObject lopObj = this; //default left operand base object is + //the first arg (object sender) of the event handler + + string[] lopParts = lop.Split (new char[] { '.' }); + if (lopParts.Length == 2) {//should search also for member of es.Source + lopObj = this.FindByName (lopParts [0]); + if (lopObj==null) + throw new Exception (string.Format("GOML:Unknown name: {0}", lopParts[0])); + //TODO: should create private member holding ref of lopObj, and emit + //a call to FindByName(lopObjName) during #ctor or in a onLoad func or evt handler + throw new Exception (string.Format("GOML:obj tree ref not yet implemented", lopParts[0])); + }else + il.Emit(OpCodes.Ldarg_0); //load sender ref onto the stack + + int i = lopParts.Length -1; + + MemberInfo lopMbi = lopObj.GetType().GetMember (lopParts[i])[0]; + OpCode lopSetOC; + dynamic lopSetMbi; + Type lopT = null; + switch (lopMbi.MemberType) { + case MemberTypes.Property: + PropertyInfo lopPi = sourceType.GetProperty (lopParts[i]); + MethodInfo dstMi = lopPi.GetSetMethod (); + lopT = lopPi.PropertyType; + lopSetMbi = dstMi; + lopSetOC = OpCodes.Callvirt; + break; + case MemberTypes.Field: + FieldInfo dstFi = sourceType.GetField(lopParts[i]); + lopT = dstFi.FieldType; + lopSetMbi = dstFi; + lopSetOC = OpCodes.Stfld; + break; + default: + throw new Exception (string.Format("GOML:member type not handle: {0}", lopParts[i])); + } + #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{ + //search for a static field in left operand type named 'rop name' + FieldInfo ropFi = lopT.GetField (rop, BindingFlags.Static|BindingFlags.Public); + if (ropFi != null) + { + il.Emit (OpCodes.Ldsfld, ropFi); + }else{ + //search if parsing methods are present + MethodInfo lopTryParseMi = lopT.GetMethod("TryParse"); + //TODO + } + } + + #endregion + + //emit left operand assignment + il.Emit(lopSetOC, lopSetMbi); + } + + il.Emit(OpCodes.Ret); + + #endregion + + Delegate del = dm.CreateDelegate(binding.Event.EventHandlerType,this); + MethodInfo addHandler = binding.Event.GetAddMethod (); + addHandler.Invoke(this, new object[] {del}); + } #region IXmlSerializable public virtual System.Xml.Schema.XmlSchema GetSchema () @@ -965,13 +1112,7 @@ namespace go continue; } if (mi.MemberType == MemberTypes.Event) { - if (!Interface.DontResoveGOML) { - Interface.GOMLResolver.Add (new DynAttribute { - Source = this, - Value = attValue, - MemberName = attName - }); - } + this.Bindings.Add (new Binding (mi, attValue)); continue; } if (mi.MemberType == MemberTypes.Property) { diff --git a/src/GraphicObjects/PrivateContainer.cs b/src/GraphicObjects/PrivateContainer.cs index e6f826dc..6cc444c5 100644 --- a/src/GraphicObjects/PrivateContainer.cs +++ b/src/GraphicObjects/PrivateContainer.cs @@ -73,6 +73,10 @@ namespace go if (child != null) child.ResolveBindings (); } + protected void ResolveBindingsWithNoRecurse () + { + base.ResolveBindings (); + } public override GraphicObject FindByName (string nameToFind) { if (Name == nameToFind) diff --git a/src/GraphicObjects/TemplatedControl.cs b/src/GraphicObjects/TemplatedControl.cs index 4dbe21e5..e203c0cf 100644 --- a/src/GraphicObjects/TemplatedControl.cs +++ b/src/GraphicObjects/TemplatedControl.cs @@ -105,6 +105,10 @@ namespace go //prevent name searching in template return nameToFind == this.Name ? this : null; } + public override void ResolveBindings () + { + base.ResolveBindingsWithNoRecurse (); + } #endregion protected virtual void loadTemplate(GraphicObject template = null)