public void GenerateSet(ILGenerator generator, OptimizationInfo optimizationInfo, PrimitiveType valueType, bool throwIfUnresolvable)
{
// The value is initially on the top of the stack but is stored in this variable
// at the last possible moment.
ILLocalVariable value = null;
var scope = this.Scope;
ILLocalVariable scopeVariable = null;
var endOfSet = generator.CreateLabel();
do
{
if (scope is DeclarativeScope)
{
// Get information about the variable.
var variable = scope.GetDeclaredVariable(this.Name);
if (variable != null)
{
// The variable was declared in this scope.
if (scope.ExistsAtRuntime == false)
{
// The scope has been optimized away. The value of the variable is stored
// in an ILVariable.
// Declare an IL local variable if no storage location has been allocated yet.
if (variable.Store == null)
variable.Store = generator.DeclareVariable(typeof(object), variable.Name);
if (value == null)
{
// The value to store is on the top of the stack - convert it to the
// storage type of the variable.
EmitConversion.Convert(generator, valueType, variable.Type, optimizationInfo);
}
else
{
// The value to store is in a temporary variable.
generator.LoadVariable(value);
EmitConversion.Convert(generator, PrimitiveType.Any, variable.Type, optimizationInfo);
}
// Store the value in the variable.
generator.StoreVariable(variable.Store);
}
else if (variable.Writable == true)
{
if (value == null)
{
// The value to store is on the top of the stack - convert it to an
// object and store it in a temporary variable.
EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
value = generator.CreateTemporaryVariable(typeof(object));
generator.StoreVariable(value);
}
// scope.Values[index] = value
if (scopeVariable == null)
EmitHelpers.LoadScope(generator);
else
generator.LoadVariable(scopeVariable);
generator.CastClass(typeof(DeclarativeScope));
generator.Call(ReflectionHelpers.DeclarativeScope_Values);
generator.LoadInt32(variable.Index);
generator.LoadVariable(value);
generator.StoreArrayElement(typeof(object));
}
else
{
// The variable exists, but is read-only.
// Pop the value off the stack (if it is still there).
if (value == null)
generator.Pop();
}
// The variable was found - no need to search any more parent scopes.
break;
}
else
{
// The variable was not defined at compile time, but may have been
// introduced by an eval() statement.
if (optimizationInfo.MethodOptimizationHints.HasEval == true)
{
if (value == null)
{
// The value to store is on the top of the stack - convert it to an
// object and store it in a temporary variable.
EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
value = generator.CreateTemporaryVariable(typeof(object));
generator.StoreVariable(value);
}
// Check the variable exists: if (scope.HasValue(variableName) == true) {
if (scopeVariable == null)
EmitHelpers.LoadScope(generator);
else
generator.LoadVariable(scopeVariable);
generator.CastClass(typeof(DeclarativeScope));
generator.LoadString(this.Name);
generator.Call(ReflectionHelpers.Scope_HasValue);
var hasValueClause = generator.CreateLabel();
generator.BranchIfFalse(hasValueClause);
// Set the value of the variable.
if (scopeVariable == null)
EmitHelpers.LoadScope(generator);
else
generator.LoadVariable(scopeVariable);
generator.CastClass(typeof(DeclarativeScope));
generator.LoadString(this.Name);
generator.LoadVariable(value);
generator.Call(ReflectionHelpers.Scope_SetValue);
generator.Branch(endOfSet);
// }
generator.DefineLabelPosition(hasValueClause);
}
}
}
else
{
if (value == null)
{
// The value to store is on the top of the stack - convert it to an
// object and store it in a temporary variable.
EmitConversion.Convert(generator, valueType, PrimitiveType.Any, optimizationInfo);
value = generator.CreateTemporaryVariable(typeof(object));
generator.StoreVariable(value);
}
if (scope.ParentScope == null)
{
// Optimization: if this is the global scope, use hidden classes to
// optimize variable access.
// Global variable modification
// ----------------------------
// __object_cacheKey = null;
// __object_property_cachedIndex = 0;
// ...
// if (__object_cacheKey != object.InlineCacheKey)
// object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
// else
// object.InlinePropertyValues[__object_property_cachedIndex] = value;
// Get a reference to the global object.
if (scopeVariable == null)
EmitHelpers.LoadScope(generator);
else
generator.LoadVariable(scopeVariable);
generator.CastClass(typeof(ObjectScope));
generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
// TODO: share these variables somehow.
var cacheKey = generator.DeclareVariable(typeof(object));
var cachedIndex = generator.DeclareVariable(typeof(int));
// Store the object into a temp variable.
var objectInstance = generator.DeclareVariable(PrimitiveType.Object);
generator.StoreVariable(objectInstance);
// if (__object_cacheKey != object.InlineCacheKey)
generator.LoadVariable(cacheKey);
generator.LoadVariable(objectInstance);
generator.Call(ReflectionHelpers.ObjectInstance_InlineCacheKey);
var elseClause = generator.CreateLabel();
generator.BranchIfEqual(elseClause);
// xxx = object.InlineSetPropertyValueIfExists("property", value, strictMode, out __object_property_cachedIndex, out __object_cacheKey)
generator.LoadVariable(objectInstance);
generator.LoadString(this.Name);
generator.LoadVariable(value);
generator.LoadBoolean(optimizationInfo.StrictMode);
generator.LoadAddressOfVariable(cachedIndex);
generator.LoadAddressOfVariable(cacheKey);
if (throwIfUnresolvable == false)
{
// Set the property value unconditionally.
generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValue);
}
else
{
// Set the property value if the property exists.
generator.Call(ReflectionHelpers.ObjectInstance_InlineSetPropertyValueIfExists);
// The return value is true if the property was defined, and false if it wasn't.
generator.BranchIfTrue(endOfSet);
}
var endOfIf = generator.CreateLabel();
generator.Branch(endOfIf);
// else
generator.DefineLabelPosition(elseClause);
// object.InlinePropertyValues[__object_property_cachedIndex] = value;
generator.LoadVariable(objectInstance);
generator.Call(ReflectionHelpers.ObjectInstance_InlinePropertyValues);
generator.LoadVariable(cachedIndex);
generator.LoadVariable(value);
generator.StoreArrayElement(typeof(object));
generator.Branch(endOfSet);
// End of the if statement
generator.DefineLabelPosition(endOfIf);
}
else
{
// Slow route.
if (scopeVariable == null)
EmitHelpers.LoadScope(generator);
else
generator.LoadVariable(scopeVariable);
generator.CastClass(typeof(ObjectScope));
generator.Call(ReflectionHelpers.ObjectScope_ScopeObject);
generator.LoadString(this.Name);
generator.LoadVariable(value);
generator.LoadBoolean(optimizationInfo.StrictMode);
if (scope.ParentScope == null && throwIfUnresolvable == false)
{
// Set the property value unconditionally.
generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValue_Object);
}
else
{
// Set the property value if the property exists.
generator.Call(ReflectionHelpers.ObjectInstance_SetPropertyValueIfExists);
// The return value is true if the property was defined, and false if it wasn't.
generator.BranchIfTrue(endOfSet);
}
}
}
// Try the parent scope.
if (scope.ParentScope != null && scope.ExistsAtRuntime == true)
{
if (scopeVariable == null)
{
scopeVariable = generator.CreateTemporaryVariable(typeof(Scope));
EmitHelpers.LoadScope(generator);
}
else
{
generator.LoadVariable(scopeVariable);
}
generator.Call(ReflectionHelpers.Scope_ParentScope);
generator.StoreVariable(scopeVariable);
}
scope = scope.ParentScope;
} while (scope != null);
// The value might be still on top of the stack.
if (value == null && scope == null)
generator.Pop();
// Throw an error if the name does not exist and throwIfUnresolvable is true.
if (scope == null && throwIfUnresolvable == true)
EmitHelpers.EmitThrow(generator, ErrorType.ReferenceError, this.Name + " is not defined", optimizationInfo);
// Release the temporary variables.
if (value != null)
generator.ReleaseTemporaryVariable(value);
if (scopeVariable != null)
generator.ReleaseTemporaryVariable(scopeVariable);
// Define a label at the end.
generator.DefineLabelPosition(endOfSet);
}