How to access locals through stack trace? (Mimicking dynamic scope)
Asked Answered
C

2

7

Background

Even though it's possible to compile C# code at runtime, it's impossible to include and run the generated code in the current scope. Instead all variables have to be passed as explicit parameters.

Compared with dynamic programming languages like Python, one could never truly replicate the complete behaviour of eval (as in this example).

x = 42
print(eval("x + 1")) # Prints 43

The question

So my question is (regardless if it's actually useful ;)) whether it's possible to mimic dynamic scope in .NET through the use of reflection.

Since .NET provides us with the Diagnostics.StackTrace class which allows us to inspect the calling methods, this question boils down to the following: (How) is it possible to reliably access the locals of calling methods?

Does the stack trace provide us with enough information to compute the memory offsets or are such things forbidden in managed code anyway?

Is such code somehow possible?

void Foo() {
   int x = 42;
   Console.WriteLine(Bar());
}

int Bar() {
   return (int)(DynamicScope.Resolve("x")); // Will access Foo's x = 42
}
Counterespionage answered 14/2, 2010 at 15:47 Comment(0)
S
5

This isn't possible. In the compiled .NET code (intermediate language), variables are represented simply as indices on the stack. For example the ldloc instruction, which loads a value of a variable takes only an unsigned int16 value as the parameter. There may be some way to do this for applications compiled in debug mode (after all, when debugging application, Visual Studio does that), but it cannot work in general.

In Phalanger (PHP compiler for .NET, which I'm partly involved in), this had to be somehow solved, because PHP language has eval (it doesn't need to provide dynamic scoping, but it needs to access variables by name). So, Phalanger detects if a function contains any use of eval and if it does, it stores all variables in Dictionary<string, object>, which is then passed to the eval function (so that it can read variables by their name). I'm afraid this is the only way to do this...

Starchy answered 14/2, 2010 at 16:12 Comment(4)
You solution for eval in Phalanger does not provide the dynamic scope behavior. Notice the code in the question: the function with eval (aka DynamicScope.Resolve) is not the one with the variable in question.Lukelukens
Oh yes, you're correct. The solution in Phalanger solves only the dynamic access part of the problem (to get dynamic scoping, we'd have to pass the dictionary around when calling any methods). I clarified the answer.Starchy
"This isn't possible ... I'm afraid this is the only way to do this" Be careful of definitive statements such as these. They are mental blocks.Quadrilateral
Sometimes definitive statements are mental blocks, sometimes they are truths (in the mathematical sense of truth). My answer of course has some implicit expectations (e.g. you won't modify .NET runtime and you won't write your own C# compiler), but without these expectations, you couldn't give any answer and if you restated them in every answer, you would sound rather silly. In any case, if you don't like the answer, you can vote it down.Starchy
Q
8

So my question is whether it's possible to mimic dynamic scope in .NET through the use of reflection.

Reflection enables run-time manipulation of items which are expressed in the metadata of an assembly.

Local variables are not items expressed in the metadata of an assembly.

The answer to your question is therefore "no".

(How) is it possible to reliably access the locals of calling methods?

A device which accesses the locals of a calling method is called a "debugger". So the answer to your question is "write yourself a debugger".

Note that debuggers do not reliably access locals in a world with code optimizations. Locals can be optimized away, the jitter can generate code which uses the same register for two different locals, stack frames can be re-used during tail calls, and so on. And of course, you'd better have the PDB file that tells the debugger what the names associated with each stack frame location are.

What your method would have to do is start up your custom debugger as a new process, and then wait for the debugger to send it a message. The debugger would then suspend the threads of the original process, do the interrogation of the stack frames, and then resume the threads of the process. It would then send messages to the debuggee that contain the information you've discovered. Since the debuggee is sitting there in a wait state waiting for the messages, it would then resume its business.

If what you want is an in-process "eval" capability, consider JScript.NET, a language that was designed for that sort of thing.

Qualification answered 14/2, 2010 at 17:28 Comment(2)
Well, the stack trace allows me to obtain names and types of the calling method's locals. Why not their values?Counterespionage
@Dario: what do parameters have to do with the stack trace? The parameters could be passed in registers. Nothing to do with the stack at all there. Also, remember, the stack trace is NOT "who called me?" I know it looks like that, but that is not actually what it is. A stack trace is "what am I going to do next?" and usually what you're going to do next is resume your caller. But not necessarily, in the case of certain optimizations.Qualification
S
5

This isn't possible. In the compiled .NET code (intermediate language), variables are represented simply as indices on the stack. For example the ldloc instruction, which loads a value of a variable takes only an unsigned int16 value as the parameter. There may be some way to do this for applications compiled in debug mode (after all, when debugging application, Visual Studio does that), but it cannot work in general.

In Phalanger (PHP compiler for .NET, which I'm partly involved in), this had to be somehow solved, because PHP language has eval (it doesn't need to provide dynamic scoping, but it needs to access variables by name). So, Phalanger detects if a function contains any use of eval and if it does, it stores all variables in Dictionary<string, object>, which is then passed to the eval function (so that it can read variables by their name). I'm afraid this is the only way to do this...

Starchy answered 14/2, 2010 at 16:12 Comment(4)
You solution for eval in Phalanger does not provide the dynamic scope behavior. Notice the code in the question: the function with eval (aka DynamicScope.Resolve) is not the one with the variable in question.Lukelukens
Oh yes, you're correct. The solution in Phalanger solves only the dynamic access part of the problem (to get dynamic scoping, we'd have to pass the dictionary around when calling any methods). I clarified the answer.Starchy
"This isn't possible ... I'm afraid this is the only way to do this" Be careful of definitive statements such as these. They are mental blocks.Quadrilateral
Sometimes definitive statements are mental blocks, sometimes they are truths (in the mathematical sense of truth). My answer of course has some implicit expectations (e.g. you won't modify .NET runtime and you won't write your own C# compiler), but without these expectations, you couldn't give any answer and if you restated them in every answer, you would sound rather silly. In any case, if you don't like the answer, you can vote it down.Starchy

© 2022 - 2024 — McMap. All rights reserved.