The poor-man's version of Set-Next-Statement (aka, SetIp) is to just forcibly set the instruction pointer register (eip on x86) to the instruction you want to execute next. However, this naive approach has several problems that ICorDebug's SetIp solves:
- the naive approach doesn't update variable homes. For example, suppose local variable 'x' starts off in ecx and then gets spilled to the stack. If you just shift the eip, you won't update the local variables to be in their new homes. ICorDebug bends over backwards to remap locals from the old variable homes to the new ones when you set-next-statement.
- SetIp must play well with other things in the function, particularly any sort of stack operations like localloc or exception handling.
In ICorDebug, if SetIp can't properly handle these things, it fails the operation. This often requires additional information not tracked in optimized code, which is why managed setip often fails in optimized code. ICorDebug exposes many SetIp failure codes (all defined in corerror.h):
It's another design issue whether we should have exposed so many different error codes. This is probably more detail than the user needs; and some of these are pretty implementation specific. The bright side is that this enables a debugger to provide a detailed error message about why SetIp may not be allowed. We have a similar issue with func-eval error codes.
Doing SetIp at the ICorDebug level:
In ICorDebug, SetIp is exposed off the ICorDebug*Frame interfaces. You can set IP based off either IL or native offsets. ICD exposes two methods: SetIp and CanSetIp. CanSetIp lets a debugger query if SetIp would succeed without actually doing the SetIp. This can be used in UI for the end-user. For example, Visual Studio lets you SetIp by dragging the current line arrow (the yellow thing). It can then change the cursor icon if the target is an invalid SetIp destination.It can also use CanSetIp to determine whether it should include a "Set Next Statement" option in a context menu.