diff --git a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index c98a4f49e..1baedd2a2 100644 --- a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -59,7 +59,17 @@ namespace ILSpy.BamlDecompiler.Rewrite foreach (var entry in eventMappings[index].value) { string xmlns = ""; // TODO : implement xmlns resolver! - element.Add(new XAttribute(xmlns + entry.EventName, entry.MethodName)); + var type = element.Annotation(); + if (type?.TypeNamespace + "." + type?.TypeName == "System.Windows.Style") + { + element.Add(new XElement(type.Namespace + "EventSetter", + new XAttribute("Event", entry.EventName), + new XAttribute("Handler", entry.MethodName))); + } + else + { + element.Add(new XAttribute(xmlns + entry.EventName, entry.MethodName)); + } } } } @@ -133,8 +143,9 @@ namespace ILSpy.BamlDecompiler.Rewrite { foreach (var section in ilSwitch.Sections) { - var events = FindEvents(section.Body); - result.Add((section.Labels, events)); + var events = new List(); + FindEvents(section.Body, events); + result.Add((section.Labels, events.ToArray())); } } else @@ -147,47 +158,126 @@ namespace ILSpy.BamlDecompiler.Rewrite continue; if (!comp.Right.MatchLdcI4(out int id)) continue; - var events = FindEvents(comp.Kind == ComparisonKind.Inequality + var inst = comp.Kind == ComparisonKind.Inequality ? ifInst.FalseInst - : ifInst.TrueInst); - result.Add((new LongSet(id), events)); + : ifInst.TrueInst; + var events = new List(); + FindEvents(inst, events); + result.Add((new LongSet(id), events.ToArray())); } } } - EventRegistration[] FindEvents(ILInstruction inst) + void FindEvents(ILInstruction inst, List events) { - var events = new List(); - switch (inst) { - case Block _: - foreach (var node in ((Block)inst).Instructions) + case Block b: + if (MatchEventSetterCreation(b, out var @event)) { - FindEvents(node, events); + events.Add(@event); + break; + } + foreach (var node in b.Instructions) + { + if (MatchSimpleEventRegistration(node, out @event)) + events.Add(@event); } - FindEvents(((Block)inst).FinalInstruction, events); break; case Branch br: - return FindEvents(br.TargetBlock); + FindEvents(br.TargetBlock, events); + break; default: - FindEvents(inst, events); + if (MatchSimpleEventRegistration(inst, out @event)) + events.Add(@event); break; } - return events.ToArray(); } - void FindEvents(ILInstruction inst, List events) + // stloc v(newobj EventSetter..ctor()) + // callvirt set_Event(ldloc v, ldsfld eventName) + // callvirt set_Handler(ldloc v, newobj RoutedEventHandler..ctor(ldloc this, ldftn eventHandler)) + // callvirt Add(callvirt get_Setters(castclass System.Windows.Style(ldloc target)), ldloc v) + // leave IL_0007 (nop) + bool MatchEventSetterCreation(Block b, out EventRegistration @event) { - CallInstruction call = inst as CallInstruction; - if (call == null || call.OpCode == OpCode.NewObj) - return; + @event = null; + var instr = b.Instructions; + if (instr.Count != 5 || !b.FinalInstruction.MatchNop()) + return false; + // stloc v(newobj EventSetter..ctor()) + if (!instr.ElementAt(0).MatchStLoc(out var v, out var initializer)) + return false; + if (!(initializer is NewObj newObj + && newObj.Method.DeclaringType.FullName == "System.Windows.EventSetter" + && newObj.Arguments.Count == 0)) + { + return false; + } + //callvirt set_Event(ldloc v, ldsfld eventName) + if (!(instr.ElementAt(1) is CallVirt setEventCall && setEventCall.Arguments.Count == 2)) + return false; + if (!setEventCall.Method.IsAccessor) + return false; + if (!setEventCall.Arguments[0].MatchLdLoc(v)) + return false; + if (setEventCall.Method.Name != "set_Event") + return false; + if (!setEventCall.Arguments[1].MatchLdsFld(out var eventField)) + return false; + string eventName = eventField.Name; + if (eventName.EndsWith("Event")) + { + eventName = eventName.Remove(eventName.Length - "Event".Length); + } + // callvirt set_Handler(ldloc v, newobj RoutedEventHandler..ctor(ldloc this, ldftn eventHandler)) + if (!(instr.ElementAt(2) is CallVirt setHandlerCall && setHandlerCall.Arguments.Count == 2)) + return false; + if (!setHandlerCall.Method.IsAccessor) + return false; + if (!setHandlerCall.Arguments[0].MatchLdLoc(v)) + return false; + if (setHandlerCall.Method.Name != "set_Handler") + return false; + if (!MatchEventHandlerCreation(setHandlerCall.Arguments[1], out string handlerName)) + return false; + @event = new EventRegistration { EventName = eventName, MethodName = handlerName }; + // callvirt Add(callvirt get_Setters(castclass System.Windows.Style(ldloc target)), ldloc v) + if (!(instr.ElementAt(3) is CallVirt addCall && addCall.Arguments.Count == 2)) + return false; + if (addCall.Method.Name != "Add") + return false; + if (!(addCall.Arguments[0] is CallVirt getSettersCall && getSettersCall.Arguments.Count == 1)) + return false; + if (!getSettersCall.Method.IsAccessor) + return false; + if (getSettersCall.Method.Name != "get_Setters") + return false; + if (!getSettersCall.Arguments[0].MatchCastClass(out var arg, out var type)) + return false; + if (type.FullName != "System.Windows.Style") + return false; + if (!(arg.MatchLdLoc(out var t) && t.Kind == VariableKind.Parameter && t.Index == 1)) + return false; + if (!addCall.Arguments[1].MatchLdLoc(v)) + return false; + return true; + } + + bool MatchSimpleEventRegistration(ILInstruction inst, out EventRegistration @event) + { + @event = null; + if (!(inst is CallInstruction call) || call.OpCode == OpCode.NewObj) + return false; - if (IsAddEvent(call, out string eventName, out string handlerName) - || IsAddAttachedEvent(call, out eventName, out handlerName)) + if (!IsAddEvent(call, out string eventName, out string handlerName) + && !IsAddAttachedEvent(call, out eventName, out handlerName)) { - events.Add(new EventRegistration { EventName = eventName, MethodName = handlerName }); + return false; } + + @event = new EventRegistration { EventName = eventName, MethodName = handlerName }; + return true; } bool IsAddAttachedEvent(CallInstruction call, out string eventName, out string handlerName) @@ -208,14 +298,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { eventName = eventName.Remove(eventName.Length - "Event".Length); } - var newObj = call.Arguments[2] as NewObj; - if (newObj == null || newObj.Arguments.Count != 2) - return false; - var ldftn = newObj.Arguments[1]; - if (ldftn.OpCode != OpCode.LdFtn && ldftn.OpCode != OpCode.LdVirtFtn) - return false; - handlerName = ((IInstructionWithMethodOperand)ldftn).Method.Name; - return true; + return MatchEventHandlerCreation(call.Arguments[2], out handlerName); } return false; @@ -235,18 +318,23 @@ namespace ILSpy.BamlDecompiler.Rewrite return false; } eventName = addMethod.Name.Substring("add_".Length); - var newObj = call.Arguments[1] as NewObj; - if (newObj == null || newObj.Arguments.Count != 2) - return false; - var ldftn = newObj.Arguments[1]; - if (ldftn.OpCode != OpCode.LdFtn && ldftn.OpCode != OpCode.LdVirtFtn) - return false; - handlerName = ((IInstructionWithMethodOperand)ldftn).Method.Name; - handlerName = XamlUtils.EscapeName(handlerName); - return true; + return MatchEventHandlerCreation(call.Arguments[1], out handlerName); } return false; } + + bool MatchEventHandlerCreation(ILInstruction inst, out string handlerName) + { + handlerName = ""; + if (!(inst is NewObj newObj) || newObj.Arguments.Count != 2) + return false; + var ldftn = newObj.Arguments[1]; + if (ldftn.OpCode != OpCode.LdFtn && ldftn.OpCode != OpCode.LdVirtFtn) + return false; + handlerName = ((IInstructionWithMethodOperand)ldftn).Method.Name; + handlerName = XamlUtils.EscapeName(handlerName); + return true; + } } }