public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(value, "value"); ExpandoClass klass; int index; ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, Value, out klass, out index); return(AddDynamicTestAndDefer( binder, klass, originalClass, new DynamicMetaObject( Expression.Call( typeof(RuntimeOps).GetMethod("ExpandoTrySetValue"), GetLimitedSelf(), Expression.Constant(klass, typeof(object)), Expression.Constant(index), Expression.Convert(value.Expression, typeof(object)), Expression.Constant(binder.Name), Expression.Constant(binder.IgnoreCase) ), BindingRestrictions.Empty ) )); }
/// <summary> /// Gets the class and the index associated with the given name. Does not update the expando object. Instead /// this returns both the original and desired new class. A rule is created which includes the test for the /// original class, the promotion to the new class, and the set/delete based on the class post-promotion. /// </summary> private ExpandoClass GetClassEnsureIndex(string name, bool caseInsensitive, ExpandoObject obj, out ExpandoClass klass, out int index) { ExpandoClass originalClass = Value.Class; index = originalClass.GetValueIndex(name, caseInsensitive, obj); if (index == ExpandoObject.AmbiguousMatchFound) { klass = originalClass; return(null); } if (index == ExpandoObject.NoMatch) { // go ahead and find a new class now... ExpandoClass newClass = originalClass.FindNewClass(name); klass = newClass; index = newClass.GetValueIndexCaseSensitive(name); Debug.Assert(index != ExpandoObject.NoMatch); return(originalClass); } else { klass = originalClass; return(null); } }
/// <summary> /// Finds or creates a new ExpandoClass given the existing set of keys /// in this ExpandoClass plus the new key to be added. Members in an /// ExpandoClass are always stored case sensitively. /// </summary> internal ExpandoClass FindNewClass(string newKey) { // just XOR the newKey hash code int hashCode = _hashCode ^ newKey.GetHashCode(); lock (this) { List <WeakReference> infos = GetTransitionList(hashCode); for (int i = 0; i < infos.Count; i++) { ExpandoClass klass = infos[i].Target as ExpandoClass; if (klass == null) { infos.RemoveAt(i); i--; continue; } if (string.Equals(klass._keys[klass._keys.Length - 1], newKey, StringComparison.Ordinal)) { // the new key is the key we added in this transition return(klass); } } // no applicable transition, create a new one string[] keys = new string[_keys.Length + 1]; Array.Copy(_keys, keys, _keys.Length); keys[_keys.Length] = newKey; ExpandoClass ec = new ExpandoClass(keys, hashCode); infos.Add(new WeakReference(ec)); return(ec); } }
public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { ExpandoClass class2; int num; ContractUtils.RequiresNotNull(binder, "binder"); ContractUtils.RequiresNotNull(value, "value"); ExpandoClass originalClass = this.GetClassEnsureIndex(binder.Name, binder.IgnoreCase, this.Value, out class2, out num); return(this.AddDynamicTestAndDefer(binder, class2, originalClass, new DynamicMetaObject(Expression.Call(typeof(RuntimeOps).GetMethod("ExpandoTrySetValue"), new Expression[] { this.GetLimitedSelf(), Expression.Constant(class2, typeof(object)), Expression.Constant(num), Expression.Convert(value.Expression, typeof(object)), Expression.Constant(binder.Name), Expression.Constant(binder.IgnoreCase) }), BindingRestrictions.Empty))); }
/// <summary> /// Promotes the class from the old type to the new type and returns the new /// ExpandoData object. /// </summary> private ExpandoData PromoteClassCore(ExpandoClass oldClass, ExpandoClass newClass) { Debug.Assert(oldClass != newClass); lock (LockObject) { if (_data.Class == oldClass) { _data = _data.UpdateClass(newClass); } return(_data); } }
public override IEnumerable <string> GetDynamicMemberNames() { ExpandoObject.ExpandoData iteratorVariable0 = this.Value._data; ExpandoClass iteratorVariable1 = iteratorVariable0.Class; for (int i = 0; i < iteratorVariable1.Keys.Length; i++) { object iteratorVariable3 = iteratorVariable0[i]; if (iteratorVariable3 != ExpandoObject.Uninitialized) { yield return(iteratorVariable1.Keys[i]); } } }
private DynamicMetaObject BindGetOrInvokeMember(DynamicMetaObjectBinder binder, string name, bool ignoreCase, DynamicMetaObject fallback, Func <DynamicMetaObject, DynamicMetaObject> fallbackInvoke) { ParameterExpression expression; ExpandoClass class2 = this.Value.Class; int num = class2.GetValueIndex(name, ignoreCase, this.Value); Expression test = Expression.Call(typeof(RuntimeOps).GetMethod("ExpandoTryGetValue"), new Expression[] { this.GetLimitedSelf(), Expression.Constant(class2, typeof(object)), Expression.Constant(num), Expression.Constant(name), Expression.Constant(ignoreCase), expression = Expression.Parameter(typeof(object), "value") }); DynamicMetaObject arg = new DynamicMetaObject(expression, BindingRestrictions.Empty); if (fallbackInvoke != null) { arg = fallbackInvoke(arg); } arg = new DynamicMetaObject(Expression.Block(new ParameterExpression[] { expression }, new Expression[] { Expression.Condition(test, arg.Expression, fallback.Expression, typeof(object)) }), arg.Restrictions.Merge(fallback.Restrictions)); return(this.AddDynamicTestAndDefer(binder, this.Value.Class, null, arg)); }
internal ExpandoObject.ExpandoData UpdateClass(ExpandoClass newClass) { if (this._dataArray.Length >= newClass.Keys.Length) { this[newClass.Keys.Length - 1] = ExpandoObject.Uninitialized; return(new ExpandoObject.ExpandoData(newClass, this._dataArray, this._version)); } int length = this._dataArray.Length; object[] destinationArray = new object[GetAlignedSize(newClass.Keys.Length)]; Array.Copy(this._dataArray, destinationArray, this._dataArray.Length); ExpandoObject.ExpandoData data = new ExpandoObject.ExpandoData(newClass, destinationArray, this._version); data[length] = ExpandoObject.Uninitialized; return(data); }
/// <summary> /// Update the associated class and increases the storage for the data array if needed. /// </summary> /// <returns></returns> internal ExpandoData UpdateClass(ExpandoClass newClass) { if (_dataArray.Length >= newClass.Keys.Length) { // we have extra space in our buffer, just initialize it to Uninitialized. this[newClass.Keys.Length - 1] = ExpandoObject.Uninitialized; return(new ExpandoData(newClass, this._dataArray, this._version)); } else { // we've grown too much - we need a new object array int oldLength = _dataArray.Length; object[] arr = new object[GetAlignedSize(newClass.Keys.Length)]; Array.Copy(_dataArray, arr, _dataArray.Length); ExpandoData newData = new ExpandoData(newClass, arr, this._version); newData[oldLength] = ExpandoObject.Uninitialized; return(newData); } }
private ExpandoClass GetClassEnsureIndex(string name, bool caseInsensitive, ExpandoObject obj, out ExpandoClass klass, out int index) { ExpandoClass class2 = this.Value.Class; index = class2.GetValueIndex(name, caseInsensitive, obj); if (index == -2) { klass = class2; return(null); } if (index == -1) { ExpandoClass class3 = class2.FindNewClass(name); klass = class3; index = class3.GetValueIndexCaseSensitive(name); return(class2); } klass = class2; return(null); }
private DynamicMetaObject BindGetOrInvokeMember(DynamicMetaObjectBinder binder, string name, bool ignoreCase, DynamicMetaObject fallback, Func <DynamicMetaObject, DynamicMetaObject> fallbackInvoke) { ExpandoClass klass = Value.Class; //try to find the member, including the deleted members int index = klass.GetValueIndex(name, ignoreCase, Value); ParameterExpression value = Expression.Parameter(typeof(object), "value"); Expression tryGetValue = Expression.Call( typeof(RuntimeOps).GetMethod("ExpandoTryGetValue"), GetLimitedSelf(), Expression.Constant(klass, typeof(object)), Expression.Constant(index), Expression.Constant(name), Expression.Constant(ignoreCase), value ); var result = new DynamicMetaObject(value, BindingRestrictions.Empty); if (fallbackInvoke != null) { result = fallbackInvoke(result); } result = new DynamicMetaObject( Expression.Block( new[] { value }, Expression.Condition( tryGetValue, result.Expression, fallback.Expression, typeof(object) ) ), result.Restrictions.Merge(fallback.Restrictions) ); return(AddDynamicTestAndDefer(binder, Value.Class, null, result)); }
/// <summary> /// Finds or creates a new ExpandoClass given the existing set of keys /// in this ExpandoClass plus the new key to be added. Members in an /// ExpandoClass are always stored case sensitively. /// </summary> internal ExpandoClass FindNewClass(string newKey) { // just XOR the newKey hash code int hashCode = _hashCode ^ newKey.GetHashCode(); lock (this) { List<WeakReference> infos = GetTransitionList(hashCode); for (int i = 0; i < infos.Count; i++) { ExpandoClass klass = infos[i].Target as ExpandoClass; if (klass == null) { infos.RemoveAt(i); i--; continue; } if (string.Equals(klass._keys[klass._keys.Length - 1], newKey, StringComparison.Ordinal)) { // the new key is the key we added in this transition return klass; } } // no applicable transition, create a new one string[] keys = new string[_keys.Length + 1]; System.Array.Copy(_keys, keys, _keys.Length); keys[_keys.Length] = newKey; ExpandoClass ec = new ExpandoClass(keys, hashCode); infos.Add(new WeakReference(ec)); return ec; } }
/// <summary> /// Sets the data for the specified class at the specified index. If the class has /// changed then a full look for the slot will be performed. If the new class does /// not have the provided slot then the Expando's class will change. Only case sensitive /// setter is supported in ExpandoObject. /// </summary> internal void TrySetValue(object indexClass, int index, object value, string name, bool ignoreCase, bool add) { ExpandoData data; object oldValue; lock (LockObject) { data = _data; if (data.Class != indexClass || ignoreCase) { // The class has changed or we are doing a case-insensitive search, // we need to get the correct index and set the value there. If we // don't have the value then we need to promote the class - that // should only happen when we have multiple concurrent writers. index = data.Class.GetValueIndex(name, ignoreCase, this); if (index == ExpandoObject.AmbiguousMatchFound) { throw Error.AmbiguousMatchInExpandoObject(name); } if (index == ExpandoObject.NoMatch) { // Before creating a new class with the new member, need to check // if there is the exact same member but is deleted. We should reuse // the class if there is such a member. int exactMatch = ignoreCase ? data.Class.GetValueIndexCaseSensitive(name) : index; if (exactMatch != ExpandoObject.NoMatch) { Debug.Assert(data[exactMatch] == Uninitialized); index = exactMatch; } else { ExpandoClass newClass = data.Class.FindNewClass(name); data = PromoteClassCore(data.Class, newClass); // After the class promotion, there must be an exact match, // so we can do case-sensitive search here. index = data.Class.GetValueIndexCaseSensitive(name); Debug.Assert(index != ExpandoObject.NoMatch); } } } // Setting an uninitialized member increases the count of available members oldValue = data[index]; if (oldValue == Uninitialized) { _count++; } else if (add) { throw Error.SameKeyExistsInExpando(name); } data[index] = value; } // Notify property changed, outside of the lock. var propertyChanged = _propertyChanged; if (propertyChanged != null && value != oldValue) { // Use the canonical case for the key. propertyChanged(this, new PropertyChangedEventArgs(data.Class.Keys[index])); } }
/// <summary> /// Constructs a new ExpandoData object with the specified class and data. /// </summary> internal ExpandoData(ExpandoClass klass, object[] data, int version) { Class = klass; _dataArray = data; _version = version; }
/// <summary> /// Constructs an empty ExpandoData object with the empty class and no data. /// </summary> private ExpandoData() { Class = ExpandoClass.Empty; _dataArray = new object[0]; }
internal ExpandoData(ExpandoClass klass, object[] data, int version) { this.Class = klass; this._dataArray = data; this._version = version; }
/// <summary> /// Adds a dynamic test which checks if the version has changed. The test is only necessary for /// performance as the methods will do the correct thing if called with an incorrect version. /// </summary> private DynamicMetaObject AddDynamicTestAndDefer(DynamicMetaObjectBinder binder, ExpandoClass klass, ExpandoClass originalClass, DynamicMetaObject succeeds) { Expression ifTestSucceeds = succeeds.Expression; if (originalClass != null) { // we are accessing a member which has not yet been defined on this class. // We force a class promotion after the type check. If the class changes the // promotion will fail and the set/delete will do a full lookup using the new // class to discover the name. Debug.Assert(originalClass != klass); ifTestSucceeds = Expression.Block( Expression.Call( null, typeof(RuntimeOps).GetMethod("ExpandoPromoteClass"), GetLimitedSelf(), Expression.Constant(originalClass, typeof(object)), Expression.Constant(klass, typeof(object)) ), succeeds.Expression ); } return(new DynamicMetaObject( Expression.Condition( Expression.Call( null, typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"), GetLimitedSelf(), Expression.Constant(originalClass ?? klass, typeof(object)) ), ifTestSucceeds, binder.GetUpdateExpression(ifTestSucceeds.Type) ), GetRestrictions().Merge(succeeds.Restrictions) )); }
private DynamicMetaObject AddDynamicTestAndDefer(DynamicMetaObjectBinder binder, ExpandoClass klass, ExpandoClass originalClass, DynamicMetaObject succeeds) { Expression ifTrue = succeeds.Expression; if (originalClass != null) { ifTrue = Expression.Block(Expression.Call(null, typeof(RuntimeOps).GetMethod("ExpandoPromoteClass"), this.GetLimitedSelf(), Expression.Constant(originalClass, typeof(object)), Expression.Constant(klass, typeof(object))), succeeds.Expression); } return(new DynamicMetaObject(Expression.Condition(Expression.Call(null, typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"), this.GetLimitedSelf(), Expression.Constant(originalClass ?? klass, typeof(object))), ifTrue, binder.GetUpdateExpression(ifTrue.Type)), this.GetRestrictions().Merge(succeeds.Restrictions))); }
private ExpandoClass?GetClassEnsureIndex(string name, bool caseInsensitive, ExpandoObject obj, out ExpandoClass @class, out int index) { // Value can be null, let it true if it is var originalClass = Value !.Class; index = originalClass.GetValueIndex(name, caseInsensitive, obj); switch (index) { case AmbiguousMatchFound: @class = originalClass; return(null); case NoMatch: // go ahead and find a new class now... var newClass = originalClass.FindNewClass(name, obj.LockObject); @class = newClass; index = newClass.GetValueIndexCaseSensitive(name, obj.LockObject); Debug.Assert(index != NoMatch); return(originalClass); default: @class = originalClass; return(null); } }
private ExpandoData() { this.Class = ExpandoClass.Empty; this._dataArray = new object[0]; }