void readPropertyBinding(Context ctx, string sourceMember, string expression) { NodeAddress sourceNA = ctx.CurrentNodeAddress; BindingDefinition bindingDef = splitBindingExp (sourceNA, sourceMember, expression); #if DEBUG_BINDING Debug.WriteLine("Property Binding: " + bindingDef.ToString()); #endif if (bindingDef.IsDataSourceBinding)//bind on data source emitDataSourceBindings (ctx, bindingDef); else ctx.StorePropertyBinding (bindingDef); }
/// <summary> /// Parses IML and build a dynamic method that will be used to instanciate one or multiple occurence of the IML file or fragment /// </summary> void parseIML(XmlTextReader reader) { Context ctx = new Context (findRootType (reader)); ctx.nodesStack.Push (new Node (ctx.RootType)); emitLoader (reader, ctx); ctx.nodesStack.Pop (); foreach (int idx in templateCachedDelegateIndices) ctx.emitCachedDelegateHandlerAddition(idx, CompilerServices.eiLogicalParentChanged); ctx.ResolveNamedTargets (); emitBindingDelegates (ctx); ctx.il.Emit (OpCodes.Ldloc_0);//load root obj to return ctx.il.Emit(OpCodes.Ret); reader.Read ();//close tag RootType = ctx.RootType; loader = (InstanciatorInvoker)ctx.dm.CreateDelegate (typeof (InstanciatorInvoker), this); }
/// <summary> /// Parse child node an generate corresponding msil /// </summary> void readChildren(XmlTextReader reader, Context ctx, int startingIdx = 0) { bool endTagReached = false; int nodeIdx = startingIdx; while (reader.Read ()) { switch (reader.NodeType) { case XmlNodeType.EndElement: endTagReached = true; break; case XmlNodeType.Element: //skip Templates if (reader.Name == "Template" || reader.Name == "ItemTemplate") { reader.Skip (); continue; } //push 2x current instance on stack for parenting and reseting loc0 to parent //loc_0 will be used for child ctx.il.Emit (OpCodes.Ldloc_0); ctx.il.Emit (OpCodes.Ldloc_0); Type t = Type.GetType ("Crow." + reader.Name); if (t == null) { Assembly a = Assembly.GetEntryAssembly (); foreach (Type expT in a.GetExportedTypes ()) { if (expT.Name == reader.Name) t = expT; } } if (t == null) throw new Exception (reader.Name + " type not found"); ctx.il.Emit (OpCodes.Newobj, t.GetConstructors () [0]);//TODO:search parameterless ctor ctx.il.Emit (OpCodes.Stloc_0);//child is now loc_0 CompilerServices.emitSetCurInterface (ctx.il); ctx.nodesStack.Push (new Node (t, nodeIdx)); emitLoader (reader, ctx); ctx.nodesStack.Pop (); ctx.il.Emit (OpCodes.Ldloc_0);//load child on stack for parenting ctx.il.Emit (OpCodes.Callvirt, ctx.nodesStack.Peek().GetAddMethod(nodeIdx)); ctx.il.Emit (OpCodes.Stloc_0); //reset local to current go nodeIdx++; break; } if (endTagReached) break; } }
void emitTemplateBindings(Context ctx, Dictionary<string, List<MemberAddress>> bindings) { //value changed dyn method DynamicMethod dm = new DynamicMethod ("dyn_tmpValueChanged", typeof (void), CompilerServices.argsValueChange, true); ILGenerator il = dm.GetILGenerator (256); //create parentchanged dyn meth in parallel to have only one loop over bindings DynamicMethod dmPC = new DynamicMethod ("dyn_InitAndLogicalParentChanged", typeof (void), CompilerServices.argsBoundDSChange, true); ILGenerator ilPC = dmPC.GetILGenerator (256); il.Emit (OpCodes.Nop); ilPC.Emit (OpCodes.Nop); System.Reflection.Emit.Label endMethod = il.DefineLabel (); il.DeclareLocal (CompilerServices.TObject); ilPC.DeclareLocal (CompilerServices.TObject);//used for checking propery less bindings ilPC.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings System.Reflection.Emit.Label cancel = ilPC.DefineLabel (); #region Unregister previous parent event handler //unregister previous parent handler if not null ilPC.Emit (OpCodes.Ldarg_2);//load old parent ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); ilPC.Emit (OpCodes.Brfalse, cancel);//old parent is null ilPC.Emit (OpCodes.Ldarg_2);//load old parent ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); //Load cached delegate ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); //add template bindings dynValueChanged delegate to new parent event ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.RemoveMethod);//call remove event #endregion ilPC.MarkLabel(cancel); #region check if new parent is null cancel = ilPC.DefineLabel (); ilPC.Emit (OpCodes.Ldarg_2);//load datasource change arg ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); ilPC.Emit (OpCodes.Brfalse, cancel);//new ds is null #endregion int i = 0; foreach (KeyValuePair<string, List<MemberAddress>> bindingCase in bindings ) { System.Reflection.Emit.Label nextTest = il.DefineLabel (); #region member name test //load source member name il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case #endregion #region destination member affectations foreach (MemberAddress ma in bindingCase.Value) { if (ma.Address.Count == 0){ Debug.WriteLine("\t\tBUG: reverse template binding in normal template binding"); continue;//template binding } //first we try to get memberInfo of new parent, if it doesn't exist, it's a propery less binding ilPC.Emit (OpCodes.Ldarg_2);//load new parent onto the stack for handler addition ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); ilPC.Emit (OpCodes.Stloc_0);//save new parent //get parent type ilPC.Emit (OpCodes.Ldloc_0);//push parent instance ilPC.Emit (OpCodes.Ldstr, bindingCase.Key);//load member name ilPC.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); ilPC.Emit (OpCodes.Stloc_1);//save memberInfo ilPC.Emit (OpCodes.Ldloc_1);//push mi for test if null System.Reflection.Emit.Label propLessReturn = ilPC.DefineLabel (); ilPC.Emit (OpCodes.Brfalse, propLessReturn); //first we have to load destination instance onto the stack, it is access //with graphic tree functions deducted from nodes topology il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event CompilerServices.emitGetChild (il, typeof(TemplatedControl), -1); CompilerServices.emitGetInstance (il, ma.Address); ilPC.Emit (OpCodes.Ldarg_2);//load destination instance to set actual value of member ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); CompilerServices.emitGetChild (ilPC, typeof(TemplatedControl), -1); CompilerServices.emitGetInstance (ilPC, ma.Address); //load new value il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); //for the parent changed dyn meth we need to fetch actual value for initialisation thrue reflexion ilPC.Emit (OpCodes.Ldloc_0);//push root instance of instanciator as parentChanged source ilPC.Emit (OpCodes.Ldloc_1);//push mi for value fetching ilPC.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); CompilerServices.emitConvert (il, ma.Property.PropertyType); CompilerServices.emitConvert (ilPC, ma.Property.PropertyType); il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); ilPC.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod()); ilPC.MarkLabel(propLessReturn); } #endregion il.Emit (OpCodes.Br, endMethod); il.MarkLabel (nextTest); i++; } //il.Emit (OpCodes.Pop); il.MarkLabel (endMethod); il.Emit (OpCodes.Ret); //store template bindings in instanciator templateBinding = dm.CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>)); #region emit LogicalParentChanged method //load new parent onto the stack for handler addition ilPC.Emit (OpCodes.Ldarg_2); ilPC.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); //Load cached delegate ilPC.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack ilPC.Emit(OpCodes.Ldfld, CompilerServices.fiTemplateBinding); //add template bindings dynValueChanged delegate to new parent event ilPC.Emit(OpCodes.Callvirt, CompilerServices.eiValueChange.AddMethod);//call add event ilPC.MarkLabel (cancel); ilPC.Emit (OpCodes.Ret); //store dschange delegate in instatiator instance for access while instancing graphic object int delDSIndex = cachedDelegates.Count; cachedDelegates.Add(dmPC.CreateDelegate (CompilerServices.ehTypeDSChange, this)); #endregion ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiLogicalParentChanged); }
void emitTemplateLoad(Context ctx, string tmpXml) { //if its a template, first read template elements using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { List<string[]> itemTemplateIds = new List<string[]> (); bool inlineTemplate = false; string templatePath = reader.GetAttribute ("Template"); reader.Read (); int depth = reader.Depth + 1; while (reader.Read ()) { if (!reader.IsStartElement () || reader.Depth > depth) continue; if (reader.Name == "Template") { inlineTemplate = true; reader.Read (); readChildren (reader, ctx, -1); } else if (reader.Name == "ItemTemplate") { string dataType = "default", datas = "", path = ""; while (reader.MoveToNextAttribute ()) { if (reader.Name == "DataType") dataType = reader.Value; else if (reader.Name == "Data") datas = reader.Value; else if (reader.Name == "Path") path = reader.Value; } reader.MoveToElement (); string itemTmpID; if (string.IsNullOrEmpty (path)) { itemTmpID = Guid.NewGuid ().ToString (); Interface.Instantiators [itemTmpID] = new ItemTemplate (new MemoryStream (Encoding.UTF8.GetBytes (reader.ReadInnerXml ())), dataType, datas); } else { if (!reader.IsEmptyElement) throw new Exception ("ItemTemplate with Path attribute may not include sub nodes"); itemTmpID = path; Interface.Instantiators [itemTmpID] = new ItemTemplate (Interface.GetStreamFromPath (itemTmpID), dataType, datas); } itemTemplateIds.Add (new string [] { dataType, itemTmpID, datas }); } } if (!inlineTemplate) {//load from path or default template ctx.il.Emit (OpCodes.Ldloc_0);//Load current templatedControl ref if (string.IsNullOrEmpty (templatePath)) { ctx.il.Emit (OpCodes.Ldnull);//default template loading } else { ctx.il.Emit (OpCodes.Ldarg_1);//load currentInterface ctx.il.Emit (OpCodes.Ldstr, templatePath); //Load template path string ctx.il.Emit (OpCodes.Callvirt,//call Interface.Load(path) CompilerServices.miIFaceLoad); } ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadTmp);//load template } //copy item templates (review this) foreach (string [] iTempId in itemTemplateIds) { ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates);//load ItemTemplates dic field ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key ctx.il.Emit (OpCodes.Ldstr, iTempId [1]);//load value ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITemp); ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miAddITemp); if (!string.IsNullOrEmpty (iTempId [2])) { //expand delegate creation ctx.il.Emit (OpCodes.Ldloc_0);//load TempControl ref ctx.il.Emit (OpCodes.Ldfld, CompilerServices.fldItemTemplates); ctx.il.Emit (OpCodes.Ldstr, iTempId [0]);//load key ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miGetITempFromDic); ctx.il.Emit (OpCodes.Ldloc_0);//load root of treeView ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miCreateExpDel); } } } }
void emitLoader(XmlTextReader reader, Context ctx) { string tmpXml = reader.ReadOuterXml (); if (ctx.nodesStack.Peek().HasTemplate) emitTemplateLoad (ctx, tmpXml); emitGOLoad (ctx, tmpXml); //emitCheckAndBindValueChanged (ctx); }
void emitPropertyBindings(Context ctx, NodeAddress origine, Dictionary<string, List<MemberAddress>> bindings) { Type origineNodeType = origine.NodeType; //value changed dyn method DynamicMethod dm = new DynamicMethod ("dyn_valueChanged" + NewId, typeof (void), CompilerServices.argsValueChange, true); ILGenerator il = dm.GetILGenerator (256); System.Reflection.Emit.Label endMethod = il.DefineLabel (); il.DeclareLocal (CompilerServices.TObject); il.Emit (OpCodes.Nop); int i = 0; foreach (KeyValuePair<string, List<MemberAddress>> bindingCase in bindings ) { System.Reflection.Emit.Label nextTest = il.DefineLabel (); #region member name test //load source member name il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); il.Emit (OpCodes.Ldstr, bindingCase.Key);//load name to test il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); il.Emit (OpCodes.Brfalse, nextTest);//if not equal, jump to next case #endregion #region destination member affectations PropertyInfo piOrig = origineNodeType.GetProperty (bindingCase.Key); Type origineType = null; if (piOrig != null) origineType = piOrig.PropertyType; foreach (MemberAddress ma in bindingCase.Value) { //first we have to load destination instance onto the stack, it is access //with graphic tree functions deducted from nodes topology il.Emit (OpCodes.Ldarg_0);//load source instance of ValueChanged event NodeAddress destination = ma.Address; if (destination.Count == 0){//template reverse binding //fetch destination instance (which is the template root) for (int j = 0; j < origine.Count ; j++) il.Emit(OpCodes.Callvirt, CompilerServices.miGetLogicalParent); }else CompilerServices.emitGetInstance (il, origine, destination); if (origineType != null && destination.Count > 0){//else, prop less binding or reverse template bind, no init requiered //for initialisation dynmeth, push destination instance loc_0 is root node in ctx ctx.il.Emit(OpCodes.Ldloc_0); CompilerServices.emitGetInstance (ctx.il, destination); //init dynmeth: load actual value from origine ctx.il.Emit (OpCodes.Ldloc_0); CompilerServices.emitGetInstance (ctx.il, origine); ctx.il.Emit (OpCodes.Callvirt, origineNodeType.GetProperty (bindingCase.Key).GetGetMethod()); } //load new value il.Emit (OpCodes.Ldarg_1); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); if (origineType == null)//property less binding, no init CompilerServices.emitConvert (il, ma.Property.PropertyType); else if (destination.Count > 0) { if (origineType.IsValueType) ctx.il.Emit(OpCodes.Box, origineType); CompilerServices.emitConvert (ctx.il, origineType, ma.Property.PropertyType); CompilerServices.emitConvert (il, origineType, ma.Property.PropertyType); ctx.il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set init value } else {// reverse templateBinding il.Emit (OpCodes.Ldstr, ma.memberName);//arg 3 of setValueWithReflexion il.Emit (OpCodes.Call, CompilerServices.miSetValWithRefx); continue; } il.Emit (OpCodes.Callvirt, ma.Property.GetSetMethod());//set value on value changes } #endregion il.Emit (OpCodes.Br, endMethod); il.MarkLabel (nextTest); i++; } il.MarkLabel (endMethod); il.Emit (OpCodes.Ret); //store and emit Add in ctx int dmIdx = cachedDelegates.Count; cachedDelegates.Add (dm.CreateDelegate (typeof(EventHandler<ValueChangeEventArgs>))); ctx.emitCachedDelegateHandlerAddition (dmIdx, CompilerServices.eiValueChange, origine); }
/// <summary> Emits handler method bindings </summary> void emitHandlerBinding(Context ctx, EventInfo sourceEvent, string expression) { NodeAddress currentNode = ctx.CurrentNodeAddress; BindingDefinition bindingDef = splitBindingExp (currentNode, sourceEvent.Name, expression); #if DEBUG_BINDING Debug.WriteLine("Event Binding: " + bindingDef.ToString()); #endif if (bindingDef.IsTemplateBinding | bindingDef.IsDataSourceBinding) { //we need to bind datasource method to source event DynamicMethod dm = new DynamicMethod ("dyn_dschangedForHandler" + NewId, typeof(void), CompilerServices.argsBoundDSChange, true); ILGenerator il = dm.GetILGenerator (256); System.Reflection.Emit.Label cancel = il.DefineLabel (); il.DeclareLocal (typeof(MethodInfo));//used to cancel binding if method doesn't exist il.Emit (OpCodes.Nop); emitRemoveOldDataSourceHandler (il, sourceEvent.Name, bindingDef.TargetMember, false); //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.Brfalse, cancel);//cancel if new datasource is null il.Emit (OpCodes.Ldarg_2);//load new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load handler method name il.Emit (OpCodes.Call, CompilerServices.miGetMethInfoWithRefx); il.Emit (OpCodes.Stloc_0);//save MethodInfo il.Emit (OpCodes.Ldloc_0);//push mi for test if null il.Emit (OpCodes.Brfalse, cancel); il.Emit (OpCodes.Ldarg_1);//load datasource change source where the event is as 1st arg of handler.add if (bindingDef.IsTemplateBinding)//fetch source instance with address CompilerServices.emitGetInstance (il, bindingDef.SourceNA); //load 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, CompilerServices.miCreateBoundDel); il.Emit (OpCodes.Callvirt, sourceEvent.AddMethod);//call add event System.Reflection.Emit.Label finish = il.DefineLabel (); il.Emit (OpCodes.Br, finish); il.MarkLabel (cancel); il.EmitWriteLine (string.Format ("Handler method '{0}' for '{1}' not found in new dataSource ", bindingDef.TargetMember, sourceEvent.Name)); il.MarkLabel (finish); il.Emit (OpCodes.Ret); //store dschange delegate in instatiator instance for access while instancing graphic object int delDSIndex = cachedDelegates.Count; cachedDelegates.Add (dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); if (bindingDef.IsDataSourceBinding) ctx.emitCachedDelegateHandlerAddition (delDSIndex, CompilerServices.eiDSChange); else //template handler binding, will be added to root parentChanged templateCachedDelegateIndices.Add (delDSIndex); } else {//normal in tree handler binding, store until tree is complete (end of parse) ctx.UnresolvedTargets.Add (new EventBinding ( bindingDef.SourceNA, sourceEvent, bindingDef.TargetNA, bindingDef.TargetMember, bindingDef.TargetName)); } }
void emitGOLoad(Context ctx, string tmpXml) { using (XmlTextReader reader = new XmlTextReader (tmpXml, XmlNodeType.Element, null)) { reader.Read (); #region Styling and default values loading if (reader.HasAttributes) { string style = reader.GetAttribute ("Style"); if (!string.IsNullOrEmpty (style)) CompilerServices.EmitSetValue (ctx.il, CompilerServices.piStyle, style); } ctx.il.Emit (OpCodes.Ldloc_0); ctx.il.Emit (OpCodes.Callvirt, CompilerServices.miLoadDefaultVals); #endregion #region Attributes reading if (reader.HasAttributes) { while (reader.MoveToNextAttribute ()) { if (reader.Name == "Style") continue; 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) { foreach (string exp in CompilerServices.splitOnSemiColumnOutsideAccolades(reader.Value)) { string trimed = exp.Trim(); if (trimed.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) compileAndStoreDynHandler (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 + "' is not a property in " + ctx.CurrentNodeType.Name); if (pi.Name == "Name") ctx.StoreCurrentName (reader.Value); if (reader.Value.StartsWith ("{", StringComparison.OrdinalIgnoreCase)) readPropertyBinding (ctx, reader.Name, reader.Value.Substring (1, reader.Value.Length - 2)); else CompilerServices.EmitSetValue (ctx.il, pi, reader.Value); } reader.MoveToElement (); } #endregion readChildren (reader, ctx); ctx.nodesStack.ResetCurrentNodeIndex (); } }
/// <summary> /// create the valuechanged handler, the datasourcechanged handler and emit event handling /// </summary> void emitDataSourceBindings(Context ctx, BindingDefinition bindingDef) { DynamicMethod dm = null; ILGenerator il = null; int dmVC = 0; PropertyInfo piSource = ctx.CurrentNodeType.GetProperty(bindingDef.SourceMember); //if no dataSource member name is provided, valuechange is not handle and datasource change //will be used as origine value string delName = "dyn_DSvalueChanged" + NewId; if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ #region create valuechanged method dm = new DynamicMethod (delName, typeof (void), CompilerServices.argsBoundValueChange, true); il = dm.GetILGenerator (256); System.Reflection.Emit.Label endMethod = il.DefineLabel (); il.DeclareLocal (CompilerServices.TObject); il.Emit (OpCodes.Nop); //load value changed member name onto the stack il.Emit (OpCodes.Ldarg_2); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCMbName); //test if it's the expected one il.Emit (OpCodes.Ldstr, bindingDef.TargetMember); il.Emit (OpCodes.Ldc_I4_4);//StringComparison.Ordinal il.Emit (OpCodes.Callvirt, CompilerServices.stringEquals); il.Emit (OpCodes.Brfalse, endMethod); //set destination member with valueChanged new value //load destination ref il.Emit (OpCodes.Ldarg_0); //load new value onto the stack il.Emit (OpCodes.Ldarg_2); il.Emit (OpCodes.Ldfld, CompilerServices.fiVCNewValue); //by default, source value type is deducted from target member type to allow //memberless binding, if targetMember exists, it will be used to determine target //value type for conversion CompilerServices.emitConvert (il, piSource.PropertyType); il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); il.MarkLabel (endMethod); il.Emit (OpCodes.Ret); //vc dyn meth is stored in a cached list, it will be bound to datasource only //when datasource of source graphic object changed dmVC = dsValueChangedDynMeths.Count; dsValueChangedDynMeths.Add (dm); #endregion } #region emit dataSourceChanged event handler //now we create the datasource changed method that will init the destination member with //the actual value of the origin member of the datasource and then will bind the value changed //dyn methode. //dm is bound to the instanciator instance to have access to cached dyn meth and delegates dm = new DynamicMethod ("dyn_dschanged", typeof (void), CompilerServices.argsBoundDSChange, true); il = dm.GetILGenerator (256); il.DeclareLocal (CompilerServices.TObject);//used for checking propery less bindings il.DeclareLocal (typeof(MemberInfo));//used for checking propery less bindings System.Reflection.Emit.Label cancel = il.DefineLabel (); System.Reflection.Emit.Label cancelInit = il.DefineLabel (); il.Emit (OpCodes.Nop); emitRemoveOldDataSourceHandler(il, "ValueChanged", delName); if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ if (bindingDef.TwoWay){ System.Reflection.Emit.Label cancelRemove = il.DefineLabel (); //remove handler if not null il.Emit (OpCodes.Ldarg_2);//load old parent il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); il.Emit (OpCodes.Brfalse, cancelRemove);//old parent is null //remove handler il.Emit (OpCodes.Ldarg_2);//1st arg load old datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCOldDS); il.Emit (OpCodes.Ldstr, "ValueChanged");//2nd arg event name il.Emit (OpCodes.Ldarg_1);//3d arg: instance bound to delegate (the source) il.Emit (OpCodes.Call, CompilerServices.miRemEvtHdlByTarget); il.MarkLabel(cancelRemove); } il.Emit (OpCodes.Ldarg_2);//load datasource change arg il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit (OpCodes.Brfalse, cancel);//new ds is null } #region fetch initial Value if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ il.Emit (OpCodes.Ldarg_2);//load new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//load member name il.Emit (OpCodes.Call, CompilerServices.miGetMembIinfoWithRefx); il.Emit (OpCodes.Stloc_1);//save memberInfo il.Emit (OpCodes.Ldloc_1);//push mi for test if null il.Emit (OpCodes.Brfalse, cancelInit);//propertyLessBinding } il.Emit (OpCodes.Ldarg_1);//load source of dataSourceChanged which is the dest instance il.Emit (OpCodes.Ldarg_2);//load new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ il.Emit (OpCodes.Ldloc_1);//push mi for value fetching il.Emit (OpCodes.Call, CompilerServices.miGetValWithRefx); } CompilerServices.emitConvert (il, piSource.PropertyType); il.Emit (OpCodes.Callvirt, piSource.GetSetMethod ()); #endregion if (!string.IsNullOrEmpty(bindingDef.TargetMember)){ il.MarkLabel(cancelInit); //check if new dataSource implement IValueChange il.Emit (OpCodes.Ldarg_2);//load new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit (OpCodes.Isinst, typeof(IValueChange)); il.Emit (OpCodes.Brfalse, cancel); il.Emit(OpCodes.Ldarg_0);//load ref to this instanciator onto the stack il.Emit (OpCodes.Ldarg_1);//load datasource change source il.Emit (OpCodes.Ldarg_2);//load new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit(OpCodes.Ldc_I4, dmVC);//load index of dynmathod il.Emit (OpCodes.Call, CompilerServices.miDSChangeEmitHelper); if (bindingDef.TwoWay){ il.Emit (OpCodes.Ldarg_1);//arg1: dataSourceChange source, the origine of the binding il.Emit (OpCodes.Ldstr, bindingDef.SourceMember);//arg2: orig member il.Emit (OpCodes.Ldarg_2);//arg3: new datasource il.Emit (OpCodes.Ldfld, CompilerServices.fiDSCNewDS); il.Emit (OpCodes.Ldstr, bindingDef.TargetMember);//arg4: dest member il.Emit (OpCodes.Call, CompilerServices.miDSReverseBinding); } il.MarkLabel (cancel); } il.Emit (OpCodes.Ret); //store dschange delegate in instatiator instance for access while instancing graphic object int delDSIndex = cachedDelegates.Count; cachedDelegates.Add(dm.CreateDelegate (CompilerServices.ehTypeDSChange, this)); #endregion ctx.emitCachedDelegateHandlerAddition(delDSIndex, CompilerServices.eiDSChange); }
/// <summary> /// Create and store in the instanciator the ValueChanged delegates /// those delegates uses grtree functions to set destination value so they don't /// need to be bound to destination instance as in the ancient system. /// </summary> void emitBindingDelegates(Context ctx) { foreach (KeyValuePair<NodeAddress,Dictionary<string, List<MemberAddress>>> bindings in ctx.Bindings ) { if (bindings.Key.Count == 0)//template binding emitTemplateBindings (ctx, bindings.Value); else emitPropertyBindings (ctx, bindings.Key, bindings.Value); } }
/// <summary> /// Compile events expression in IML attributes, and store the result in the instanciator /// Those handlers will be bound when instatiing /// </summary> void compileAndStoreDynHandler(Context ctx, EventInfo sourceEvent, string expression) { //store event handler dynamic method in instanciator int dmIdx = cachedDelegates.Count; cachedDelegates.Add (CompilerServices.compileDynEventHandler (sourceEvent, expression, ctx.CurrentNodeAddress)); ctx.emitCachedDelegateHandlerAddition(dmIdx, sourceEvent); }