Jan 13, 2010 at 1:34 PM
Edited Jan 13, 2010 at 1:34 PM
Things have been coming along nicely with my language implementation, got most of the stuff I want in - but now I've encountered a bit of a problem: I have a context object that holds stuff like built in objects, pre-defined functions, classes, etc. These
can be changed out during runtime (the context object is not static, it can be changed by the code that is currently executing). It's also used to expose parts of the BCL to my language (sort of what SymPL does with import, etc - but a bit more involved).
Now the thing is that this object is the only and only argument to the lambda compiled for the expression tree, like this:
Still nothing weird going on, when the compiled lambda is called from C# to execute the expression tree you just pass in a Context object (which you can setup yourself before hand, to choose what you want to expose to the language, what built in objects
should be available, etc.) - this is exactly what SymPL does for its globals object, etc.
The problem here is that sometimes I need access this context object from within my binders (especially my ConvertBinder, but that's not really the point) - to access some functionality one of the built in native objects, or when an object is automatically
created during runtime and it needs to inherit from the base 'MyLangObject'-base class.
I've tried pushing the ContextParamExpr (since it's at the top level) on at the beginning on all the arguments on the Invoke and InvokeMember binders, and then pull it of the args array passed into the binder. Still doesn't solve the problem with the SetMember,
GetMember and Convert binders - and it's feels like a really ugly hack.
I've also tried passing a context object into the compilation itself, passing at as an argument to a overloaded constructor on the binders, but this ties a compilation of a specific source code to this exact context object - so I need to compile the source
code for each context object instead of using the same compiled lambda and passing in a different context object instead.
My third solution is to instead of passing in the context object to the binder constructors directly I pass in a "ContextProvider" object which just has one field called "Context" which allows me to switch out the context between different
executions of the same code unit (and the binders access the current context through contextProvider.Context). But this means I can't execute a compiled unit in any form of parallel fashion because I change the ContextProvider.Context field to the Context
for the currently executing environment, but if I started a second execution of the same compiled unit it would change the contextProvider.Context object for that compilation unit and the previous, already ongoing, execution would have it's contextProvider.Context
object changed mid-runtime.
Does anyone have a good solution to this? Do I need to drill down even further then just creating my own binders to achieve this?