MSIL : Superfluous branch - cil

Consider this C# snippet:
static string input = null;
static string output = null;
static void Main(string[] args)
{
input = "input";
output = CallMe(input);
}
public static string CallMe(string input)
{
output = "output";
return output;
}
Dissassembling using Reflector shows:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
L_0000: nop
L_0001: ldstr "input"
L_0006: stsfld string Reflector_Test.Program::input
L_000b: ldsfld string Reflector_Test.Program::input
L_0010: call string Reflector_Test.Program::CallMe(string)
L_0015: stsfld string Reflector_Test.Program::output
L_001a: ret
}
.method public hidebysig static string CallMe(string input) cil managed
{
.maxstack 1
.locals init (
[0] string CS$1$0000)
L_0000: nop
L_0001: ldstr "output"
L_0006: stsfld string Reflector_Test.Program::output
L_000b: ldsfld string Reflector_Test.Program::output
L_0010: stloc.0
L_0011: br.s L_0013
L_0013: ldloc.0
L_0014: ret
}
The piece that puzzles me is:
L_0010: stloc.0
L_0011: br.s L_0013
L_0013: ldloc.0
It stores the item, branches to the next line (which would have been executed anyway) and then loads it again.
Is there a reason for this?

This only happens in Debug, not in Release. I suspect its to assist during debugging. It perhaps allows you to chuck breakpoints mid statement and see the return value.
Note the release version has much more concise IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.maxstack 8
L_0000: ldstr "input"
L_0005: stsfld string Reflector_Test.Program::input
L_000a: ldsfld string Reflector_Test.Program::input
L_000f: call string Reflector_Test.Program::CallMe(string)
L_0014: stsfld string Reflector_Test.Program::output
L_0019: ret
}
.method public hidebysig static string CallMe(string input) cil managed
{
.maxstack 8
L_0000: ldstr "output"
L_0005: stsfld string Reflector_Test.Program::output
L_000a: ldsfld string Reflector_Test.Program::output
L_000f: ret
}

My guess is that this is boilerplate code for executing a return statement, the compiler executes an unconditional jump to the last line, and loads the return value into a register before executing the ret. The JIT will optimise it better, I think the compiler doesn't bother doing any optimisations.

Related

When attaching agent to running process, bytebuddy transformer doesn't seem to take effect

The code of my program to be attached is as below.
public class Foo {
}
public class TestEntry {
public TestEntry() {
}
public static void main(String[] args) throws Exception {
try
{
while(true)
{
System.out.println(new Foo().toString());
Thread.sleep(1000);
}
}
catch(Exception e)
{}
}
}
What I attempt to do is to make Foo.toString() returns 'test' by using the following agent.
public class InjectionAgent {
public InjectionAgent() {
}
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
public static void premain(String args, Instrumentation inst) throws Exception
{
System.out.println("premain Args:" + args);
new AgentBuilder.Default()
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("test"));
}
}).installOn(inst);
}
}
I notice that, it was successful when I using -javaagent way, whereas attach way failed, here is code for attach.
public class Injection {
public Injection() {
}
public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException, InterruptedException {
VirtualMachine vm = null;
String agentjarpath = args[0];
vm = VirtualMachine.attach(args[1]);
vm.loadAgent(agentjarpath, "This is Args to the Agent.");
vm.detach();
}
}
I tried to add AgentBuilder.Listener.StreamWriting.toSystemOut() to the agent, after attaching, the output of TestEntry shows
[Byte Buddy] DISCOVERY Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] TRANSFORM Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
[Byte Buddy] COMPLETE Foo [sun.misc.Launcher$AppClassLoader#33909752, null, loaded=true]
Foo#7f31245a
Foo#6d6f6e28
Foo#135fbaa4
Foo#45ee12a7
Foo#330bedb4
==================================Update=====================================
I defined a public method 'Bar' in Foo like this
public class Foo {
public String Bar()
{
return "Bar";
}
}
and then I was trying to make Foo.Bar() returns "modified" in the following way:
public static void agentmain(String args, Instrumentation inst) throws Exception
{
System.out.println("agentmain Args:" + args);
premain(args, inst);
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.type(ElementMatchers.named("Foo"))
.transform(new AgentBuilder.Transformer() {
#Override
public Builder<?> transform(Builder<?> arg0, TypeDescription arg1,
ClassLoader arg2, JavaModule arg3) {
return arg0.visit(Advice.to(InjectionTemplate.class).on(ElementMatchers.named("Bar")));
}
})
.installOn(inst);
}
static class InjectionTemplate {
#Advice.OnMethodExit
static void exit(#Advice.Return String self) {
System.out.println(self.toString() + " " + self.getClass().toString());
self = new String("modified");
}
}
but I got this error:
java.lang.IllegalStateException: Cannot write to read-only parameter class java.lang.String at 1
any suggestions?
It does not seem like you are using redefinition for your agent. You can activate it using:
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
The last part is required on most JVMs (with the notable exception of the dynamic code evolution VM, a custom build of HotSpot). It tells Byte Buddy to not add fields or methods, what most VMs do not support.
In this case, it is no longer possible to invoke the original implementation of a method what is however not required for your FixedValue. Typically, users of Byte Buddy take advantage of Advice when creating an agent that applies dynamic transformations of classes.

No Events when using reflection on Excel Application COM Object

I have an Excel template that I use to link to my application via this method:
Excel VBA:
objApp.SetExcelApp Application
Inside my application code, I want to use reflection to hook up to the WorkbookBeforeClose event on the Excel Application so I can do stuff when that event fires:
VB.NET:
Public Sub SetExcelApp(ByRef objExcel As Object)
Dim evWorkbookBeforeClose As Reflection.EventInfo = objExcel.GetType().GetEvent("WorkbookBeforeClose")
Dim mEvents As Reflection.EventInfo() = objExcel.GetType().GetEvents()
' do stuff
End Sub
Surprisingly, evWorkbookBeforeClose is Nothing - even mEvents is Nothing!
I can use methods and properties completely fine. Like objExcel.ActiveWorkbook and objExcel.Run("MyVBAMethod")
Anyone have ideas why this doesn't work? Is this not possible using reflection? Do I have to add a reference to the Excel interop?
EDIT: So if I add the Excel interop as a reference, it will work fine, since I can just use AddHandler on the event. But I'd still like to know why this doesn't work using reflection...
An interesting thing I saw was even when I changed this to objExcel As Microsoft.Office.Interop.Excel.Application, doing the above calls (GetEvent/GetEvents) still both return Nothing!
The events can be added using the System.Runtime.InteropServices.ComEventsHelper class.
E.g.
// source: http://www.guysmithferrier.com/downloads/csharp4.pdf
public static void ExcelExample() {
Type ty = Type.GetTypeFromProgID("Excel.Application");
Object o1 = Activator.CreateInstance(ty); // COM Object
o1.GetType().InvokeMember("Visible", BindingFlags.SetProperty, null, o1, new Object[] { true });
// Sh = Sheet?
// Target = Range
// 0x616 is the SelectionChanged event
ComEventsHelper.Combine(o1, new Guid("00024413-0000-0000-C000-000000000046"), 0x616, new Action<Object,Object>((Sh, Target) => {
int breakPoint = 1;
}));
}
public static void WordExample() {
Type ty = Type.GetTypeFromProgID("Word.Application");
Object o1 = Activator.CreateInstance(ty); // COM Object
o1.GetType().InvokeMember("Visible", BindingFlags.SetProperty, null, o1, new Object[] { true });
//var wdg4 = new Guid("00020A01-0000-0000-C000-000000000046"); // ApplicationEvents4 (Word)
//var wdg3 = new Guid("00020A00-0000-0000-C000-000000000046"); // ApplicationEvents3 (Word)
//var wdg2 = new Guid("000209FE-0000-0000-C000-000000000046"); // ApplicationEvents2 (Word)
var wdg1 = new Guid("000209F7-0000-0000-C000-000000000046"); // ApplicationEventsInterface (Word)
// 2 = Quit event
ComEventsHelper.Combine(o1, wdg1, 2, new Action(() => {
int breakPoint = 1;
}));
// 4 = Document open
ComEventsHelper.Combine(o1, wdg1, 4, new Action<Object>((Doc) => {
int breakPoint = 1;
}));
}
Seems like there is no MSDN reference for the dispatch ids (dispid) values. I found a few sources online, so I'll post them here:
Source: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3625e60d-0e9c-4871-a0e8-bcedbbb31ce0/is-there-an-fixed-version-of-microsoftofficeinteropworddll-on-msdn?forum=worddev
Word:
[DispId(0x00000001)]
void Startup();
[DispId(0x00000002)]
void Quit();
[DispId(0x00000003)]
void DocumentChange();
[DispId(0x00000004)]
void DocumentOpen(Word.Document Doc);
[DispId(0x00000006)]
void DocumentBeforeClose(Word.Document Doc, ref bool Cancel);
[DispId(0x00000007)]
void DocumentBeforePrint(Word.Document Doc, ref bool Cancel);
[DispId(0x00000008)]
void DocumentBeforeSave(Word.Document Doc, ref bool SaveAsUI, ref bool Cancel);
[DispId(0x00000009)]
void NewDocument(Word.Document Doc);
[DispId(0x0000000a)]
void WindowActivate(Word.Document Doc, Word.Window Wn);
[DispId(0x0000000b)]
void WindowDeactivate(Word.Document Doc, Word.Window Wn);
[DispId(0x0000000c)]
void WindowSelectionChange(Word.Selection Sel);
[DispId(0x0000000d)]
void WindowBeforeRightClick(Word.Selection Sel, ref bool Cancel);
[DispId(0x0000000e)]
void WindowBeforeDoubleClick(Word.Selection Sel, ref bool Cancel);
[DispId(0x0000000f)]
void EPostagePropertyDialog(Word.Document Doc);
[DispId(0x00000010)]
void EPostageInsert(Word.Document Doc);
[DispId(0x00000011)]
void MailMergeAfterMerge(Word.Document Doc, Word.Document DocResult);
[DispId(0x00000012)]
void MailMergeAfterRecordMerge(Word.Document Doc);
[DispId(0x00000013)]
void MailMergeBeforeMerge(Word.Document Doc, int StartRecord, int endRecord, ref bool Cancel);
[DispId(0x00000014)]
void MailMergeBeforeRecordMerge(Word.Document Doc, ref bool Cancel);
[DispId(0x00000015)]
void MailMergeDataSourceLoad(Word.Document Doc);
[DispId(0x00000016)]
void MailMergeDataSourceValidate(Word.Document Doc, ref bool Handled);
[DispId(0x00000017)]
void MailMergeWizardSendToCustom(Word.Document Doc);
[DispId(0x00000018)]
void MailMergeWizardStateChange(Word.Document Doc, ref int FromState, ref int ToState, ref bool Handled);
[DispId(0x00000019)]
void WindowSize(Word.Document Doc, Word.Window Wn);
Excel:
Source: http://www.codeforge.com/read/240027/AppEvents.java__html
#DISPID(1565)
public void newWorkbook(Wb)
#DISPID(1558)
public void sheetSelectionChange(Sh, Target)
#DISPID(1559)
public void sheetBeforeDoubleClick(Sh, Target, Cancel);
#DISPID(1560)
public void sheetBeforeRightClick(Sh, Target, Cancel);
#DISPID(1561)
public void sheetActivate(Sh)
#DISPID(1562)
public void sheetDeactivate(Sh)
#DISPID(1563)
public void sheetCalculate(Sh)
#DISPID(1564)
public void sheetChange(Sh, Target)
#DISPID(1567)
public void workbookOpen(Wb)
#DISPID(1568)
public void workbookActivate(Wb)
#DISPID(1569)
public void workbookDeactivate(Wb)
#DISPID(1570)
public void workbookBeforeClose(Wb, Cancel)
#DISPID(1571)
public void workbookBeforeSave(Wb, SaveAsUI, Cancel)
#DISPID(1572)
public void workbookBeforePrint(Wb, Cancel)
#DISPID(1573)
public void workbookNewSheet(Wb, Sh)
#DISPID(1574)
public void workbookAddinInstall(Wb)
#DISPID(1575)
public void workbookAddinUninstall(Wb)
#DISPID(1554)
public void windowResize(Wb, Window)
#DISPID(1556)
public void windowActivate(Wb, Window)
#DISPID(1557)
public void windowDeactivate(Wb, Window)
#DISPID(1854)
public void sheetFollowHyperlink(Sh, Hyperlink)
#DISPID(2157)
public void sheetPivotTableUpdate(Sh, PivotTable)
#DISPID(2160)
public void workbookPivotTableCloseConnection(Wb, PivotTable)
#DISPID(2161)
public void workbookPivotTableOpenConnection(Wb, PivotTable)
#DISPID(2289)
public void workbookSync(Wb, SyncEventType)
#DISPID(2290)
public void workbookBeforeXmlImport(Wb, XmlMap, Url, IsRefresh, Cancel)
#DISPID(2291)
public void workbookAfterXmlImport(Wb, XmlMap, IsRefresh, Result)
#DISPID(2292)
public void workbookBeforeXmlExport(Wb, XmlMap, Url, Cancel)
#DISPID(2293)
public void workbookAfterXmlExport(Wb, XmlMap, Url, Result)
#DISPID(2611)
public void workbookRowsetComplete(Wb, Description, Sheet, Success)
#DISPID(2612)
public void afterCalculate()

Using IOperationBehavior to supply a WCF parameter

This is my first step into the world of stackoverflow, so apologies if I cock anything up.
I'm trying to create a WCF Operation which has a parameter that is not exposed to the outside world, but is instead automatically passed into the function.
So the world sees this: int Add(int a, int b)
But it is implemented as: int Add(object context, int a, int b)
Then, the context gets supplied by the system at run-time. The example I'm working with is completely artificial, but mimics something that I'm looking into in a real-world scenario.
I'm able to get close, but not quite the whole way there.
First off, I created a simple method and wrote an application to confirm it works. It does. It returns a + b and writes the context as a string to my debug. Yay.
[OperationContract]
int Add(object context, int a, int b);
I then wrote the following code:
public class SupplyContextAttribute : Attribute, IOperationBehavior
{
public void Validate(OperationDescription operationDescription)
{
if (!operationDescription.Messages.Any(m => m.Body.Parts.First().Name == "context"))
throw new FaultException("Parameter 'context' is missing.");
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new SupplyContextInvoker(dispatchOperation.Invoker);
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
// Remove the 'context' parameter from the inbound message
operationDescription.Messages[0].Body.Parts.RemoveAt(0);
}
}
public class SupplyContextInvoker : IOperationInvoker
{
readonly IOperationInvoker _invoker;
public SupplyContextInvoker(IOperationInvoker invoker)
{
_invoker = invoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs().Skip(1).ToArray();
}
private object[] IntroduceContext(object[] inputs)
{
return new[] { "MyContext" }.Concat(inputs).ToArray();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
return _invoker.Invoke(instance, IntroduceContext(inputs), out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _invoker.InvokeBegin(instance, IntroduceContext(inputs), callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _invoker.IsSynchronous; }
}
}
And my WCF operation now looks like this:
[OperationContract, SupplyContext]
int Amend(object context, int a, int b);
My updated references no longer show the 'context' parameter, which is exactly what I want.
The trouble is that whenver I run the code, it gets past the AllocateInputs and then throws an Index was outside the bounds of the Array. error somewhere in the WCF guts.
I've tried other things, and I find that I can successfully change the type of the parameter and rename it and have my code work. But the moment I remove the parameter it falls over.
Can anyone give me some idea of how to get this to work (or if it can be done at all).
Well, I figured it out on my own. The MessagePartDescription has an Index property. I just need to resynch these values.
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
var parts = operationDescription.Messages[0].Body.Parts;
parts.RemoveAt(0);
for (int i = 0; i < parts.Count; i++)
parts[i].Index = i;
}

Does Mono have the equivalent of ILDASM?

Is there an equivalent of ILDASM for Mono?
Yes, monodis is Mono's equivalent for ildasm.
$ cat a.cs
public class Foo
{
public static void Main()
{
System.Console.WriteLine("Hello world");
}
}
$ monodis a.exe
.assembly extern mscorlib
{
.ver 1:0:5000:0
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'a'
{
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module a.exe // GUID = {034ADE1A-22D2-4B2B-960B-AE9DBFB2FCE7}
.class public auto ansi beforefieldinit Foo
extends [mscorlib]System.Object
{
// method line 1
.method public hidebysig specialname rtspecialname
instance default void '.ctor' () cil managed
{
// Method begins at RVA 0x20ec
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void object::'.ctor'()
IL_0006: ret
} // end of method Foo::.ctor
// method line 2
.method public static hidebysig
default void Main () cil managed
{
// Method begins at RVA 0x20f4
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "Hello world"
IL_0005: call void class [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Foo::Main
} // end of class Foo

Callback function is not executed in an asynchronous call

I am trying to make a simple asynchronous call with WCF, but the callback function is never executed. Can anyone tell me what is wrong with the code?
I am using visual studio 2008 with .Net 3.5
Service code
[ServiceContract]
public interface IService1
{
[OperationContract(AsyncPattern = true) ]
IAsyncResult BeginGetData(string value, AsyncCallback callback, object state);
string EndGetData(IAsyncResult result);
}
public class Service1 : IService1
{
#region IService1 Members
public IAsyncResult BeginGetData(string value, AsyncCallback callback, object state)
{
return new CompletedAsyncResult<string>(value, state);
}
public string EndGetData(IAsyncResult r)
{
CompletedAsyncResult<string> result = r as CompletedAsyncResult<string>;
return result.Data;
}
#endregion
}
Client side code
class Program
{
static void Main(string[] args)
{
Service1Client client = new Service1Client();
Console.WriteLine("Start async call");
IAsyncResult result = client.BeginGetData("abc", callback, null);
Console.ReadLine();
}
static void callback(IAsyncResult result)
{
string a = "in callback";
Console.WriteLine(a);
}
}
You need call callback explicitly.
IAsyncResult result = client.BeginGetData("abc", callback, null);
callback(result);
Console.ReadLine();
see reference here.
http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx