Exemple #1
0
            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
                               )
                           ));
            }
Exemple #2
0
            /// <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);
                }
            }
Exemple #3
0
        /// <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);
            }
        }
Exemple #4
0
            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)));
            }
Exemple #5
0
        /// <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);
            }
        }
Exemple #6
0
            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]);
                    }
                }
            }
Exemple #7
0
            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));
            }
Exemple #8
0
            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);
            }
Exemple #9
0
 /// <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);
     }
 }
Exemple #10
0
            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);
            }
Exemple #11
0
            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;
            }
        }
Exemple #13
0
        /// <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]));
            }
        }
Exemple #14
0
 /// <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;
 }
Exemple #15
0
 /// <summary>
 /// Constructs an empty ExpandoData object with the empty class and no data.
 /// </summary>
 private ExpandoData()
 {
     Class      = ExpandoClass.Empty;
     _dataArray = new object[0];
 }
Exemple #16
0
 internal ExpandoData(ExpandoClass klass, object[] data, int version)
 {
     this.Class      = klass;
     this._dataArray = data;
     this._version   = version;
 }
Exemple #17
0
            /// <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)
                           ));
            }
Exemple #18
0
            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)));
            }
Exemple #19
0
            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);
                }
            }
Exemple #20
0
 private ExpandoData()
 {
     this.Class      = ExpandoClass.Empty;
     this._dataArray = new object[0];
 }