public Expression Bind(
DynamicMetaObjectBinder payload,
IEnumerable<Expression> parameters,
DynamicMetaObject[] args,
out DynamicMetaObject deferredBinding)
{
// The lock is here to protect this instance of the binder from itself
// when called on multiple threads. The cost in time of a single lock
// on a single thread appears to be negligible and dominated by the cost
// of the bind itself. My timing of 4000 consecutive dynamic calls with
// bind, where the body of the called method is empty, are as follows
// (five samples):
//
// Without lock() With lock()
// = 00:00:10.7597696 = 00:00:10.7222606
// = 00:00:10.0711116 = 00:00:10.1818496
// = 00:00:09.9905507 = 00:00:10.1628693
// = 00:00:09.9892183 = 00:00:10.0750007
// = 00:00:09.9253234 = 00:00:10.0340266
//
// ...subsequent calls that were cache hits, i.e., already bound, took less
// than 1/1000 sec for the whole 4000 of them.
lock (_bindLock)
{
// this is a strategy for realizing correct binding when the symboltable
// finds a name collision across different types, e.g. one dynamic binding
// uses a type "N.T" and now a second binding uses a different type "N.T".
// In order to make this work, we have to reset the symbol table and begin
// the second binding over again when we detect the collision. So this is
// something like a longjmp to the beginning of binding. For a single binding,
// if we have to do this more than once, we give an ICE--this would be a
// scenario that needs to know about both N.T's simultaneously to work.
// See SymbolTable.LoadSymbolsFromType for more information.
try
{
return BindCore(payload, parameters, args, out deferredBinding);
}
catch (ResetBindException)
{
Reset();
try
{
return BindCore(payload, parameters, args, out deferredBinding);
}
catch (ResetBindException)
{
Reset();
Debug.Assert(false, "More than one symbol table name collision in a single binding");
throw Error.InternalCompilerError();
}
}
}
}