示例#1
0
        /// <summary>
        /// Finds or creates a new ExpandoClass given the existing set of keys
        /// in this ExpandoClass plus the new key to be added.
        /// </summary>
        internal ExpandoClass FindNewClass(string newKey, bool ignoreCase) {
            // 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, GetStringComparison(ignoreCase))) {
                        // 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;
            }
        }
示例#2
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.
        /// </summary>
        internal void SetValue(ExpandoClass klass, int index, bool caseInsensitive, object value) {
            Debug.Assert(index != -1);

            lock (this) {
                ExpandoData data = _data;

                if (data.Class != klass) {
                    // the class has changed, 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.
                    string name = klass.GetIndexName(index);
                    index = data.Class.GetValueIndex(name, caseInsensitive);
                    if (index == -1) {
                        ExpandoClass newClass = data.Class.FindNewClass(name, caseInsensitive);

                        data = PromoteClassWorker(data.Class, newClass);
                        index = data.Class.GetValueIndex(name, caseInsensitive);

                        Debug.Assert(index != -1);
                    }
                }

                data.Data[index] = value;
            }           
        }              
示例#3
0
            public override MetaObject BindDeleteMember(DeleteMemberBinder binder)
            {
                ContractUtils.RequiresNotNull(binder, "binder");

                ExpandoClass klass;
                int          index;

                ExpandoClass originalClass = GetClassEnsureIndex(binder.Name, binder.IgnoreCase, out klass, out index);

                string methodName = binder.IgnoreCase ? "ExpandoDeleteValueIgnoreCase" : "ExpandoDeleteValue";

                return(new MetaObject(
                           AddDynamicTestAndDefer(
                               binder,
                               new MetaObject[] { this },
                               klass,
                               originalClass,
                               Expression.Convert(
                                   Expression.Call(
                                       typeof(RuntimeOps).GetMethod(methodName),
                                       GetLimitedSelf(),
                                       Expression.Constant(klass),
                                       Expression.Constant(index)
                                       ),
                                   typeof(object)
                                   )
                               ),
                           GetRestrictions()
                           ));
            }
示例#4
0
        /// <summary>
        /// Finds or creates a new ExpandoClass given the existing set of keys
        /// in this ExpandoClass plus the new key to be added.
        /// </summary>
        internal ExpandoClass FindNewClass(string newKey, bool ignoreCase)
        {
            // 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, GetStringComparison(ignoreCase)))
                    {
                        // 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);
            }
        }
示例#5
0
        /// <summary>
        /// Gets the data stored for the specified class at the specified index.  If the
        /// class has changed a full lookup for the slot will be performed and the correct
        /// value will be retrieved.
        /// </summary>
        internal bool DeleteValue(ExpandoClass klass, int index, bool caseInsensitive)
        {
            Debug.Assert(index != -1);

            lock (this) {
                ExpandoData data = _data;

                if (data.Class != klass)
                {
                    // the class has changed, we need to get the correct index.  If there is
                    // no associated index we simply can't have the value and we return
                    // false.
                    index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive);
                    if (index == -1)
                    {
                        return(false);
                    }
                }

                object curValue = data.Data[index];
                data.Data[index] = Uninitialized;

                return(curValue != Uninitialized);
            }
        }
示例#6
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.
        /// </summary>
        internal void SetValue(ExpandoClass klass, int index, bool caseInsensitive, object value)
        {
            Debug.Assert(index != -1);

            lock (this) {
                ExpandoData data = _data;

                if (data.Class != klass)
                {
                    // the class has changed, 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.
                    string name = klass.GetIndexName(index);
                    index = data.Class.GetValueIndex(name, caseInsensitive);
                    if (index == -1)
                    {
                        ExpandoClass newClass = data.Class.FindNewClass(name, caseInsensitive);

                        data  = PromoteClassWorker(data.Class, newClass);
                        index = data.Class.GetValueIndex(name, caseInsensitive);

                        Debug.Assert(index != -1);
                    }
                }

                data.Data[index] = value;
            }
        }
示例#7
0
        /// <summary>
        /// Gets the data stored for the specified class at the specified index.  If the
        /// class has changed a full lookup for the slot will be performed and the correct
        /// value will be retrieved.
        /// </summary>
        internal object GetValue(ExpandoClass klass, int index, bool caseInsensitive)
        {
            Debug.Assert(index != -1);

            // read the data now.  The data is immutable so we get a consistent view.
            // If there's a concurrent writer they will replace data and it just appears
            // that we won the race
            ExpandoData data = _data;
            object      res  = Uninitialized;

            if (data.Class != klass)
            {
                // the class has changed, we need to get the correct index and return
                // the value there.
                index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive);
            }

            // index is now known to be correct
            res = data.Data[index];

            if (res == Uninitialized)
            {
                throw new MissingMemberException(klass.GetIndexName(index));
            }

            return(res);
        }
示例#8
0
        /// <summary>
        /// Promotes the class from the old type to the new type and returns the new
        /// ExpandoData object.
        /// </summary>
        private ExpandoData PromoteClassWorker(ExpandoClass oldClass, ExpandoClass newClass)
        {
            Debug.Assert(oldClass != newClass);

            lock (this) {
                if (_data.Class == oldClass)
                {
                    _data = new ExpandoData(newClass, newClass.GetNewKeys(_data.Data));
                }
                return(_data);
            }
        }
示例#9
0
            public override MetaObject BindGetMember(GetMemberBinder binder)
            {
                ContractUtils.RequiresNotNull(binder, "binder");

                ExpandoClass klass = Value.Class;

                int    index      = klass.GetValueIndex(binder.Name, binder.IgnoreCase);
                string methodName = binder.IgnoreCase ? "ExpandoGetValueIgnoreCase" : "ExpandoGetValue";

                Expression target;

                if (index == -1)
                {
                    // the key does not exist, report a MissingMemberException
                    target = Expression.Convert(
                        Expression.Throw(
                            Expression.New(
                                typeof(MissingMemberException).GetConstructor(new Type[] { typeof(string) }),
                                Expression.Constant(binder.Name)
                                )
                            ),
                        typeof(object)
                        );
                }
                else
                {
                    target = Expression.Call(
                        typeof(RuntimeOps).GetMethod(methodName),
                        GetLimitedSelf(),
                        Expression.Constant(klass),
                        Expression.Constant(index)
                        );
                }

                // add the dynamic test for the target
                return(new MetaObject(
                           AddDynamicTestAndDefer(
                               binder,
                               new MetaObject[] { this },
                               klass,
                               null,
                               target
                               ),
                           GetRestrictions()
                           ));
            }
示例#10
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 ignoreCase, out ExpandoClass klass, out int index)
            {
                ExpandoClass originalClass = Value.Class;

                index = originalClass.GetValueIndex(name, ignoreCase);
                if (index == -1)
                {
                    // go ahead and find a new class now...
                    ExpandoClass newClass = originalClass.FindNewClass(name, ignoreCase);

                    klass = newClass;
                    index = newClass.GetValueIndex(name, ignoreCase);

                    Debug.Assert(index != -1);
                    return(originalClass);
                }
                else
                {
                    klass = originalClass;
                    return(null);
                }
            }
示例#11
0
        /// <summary>
        /// Gets the data stored for the specified class at the specified index.  If the
        /// class has changed a full lookup for the slot will be performed and the correct
        /// value will be retrieved.
        /// </summary>
        internal object GetValue(ExpandoClass klass, int index, bool caseInsensitive) {
            Debug.Assert(index != -1);

            // read the data now.  The data is immutable so we get a consistent view.
            // If there's a concurrent writer they will replace data and it just appears
            // that we won the race
            ExpandoData data = _data;
            object res = Uninitialized;
            if (data.Class != klass) {
                // the class has changed, we need to get the correct index and return
                // the value there.
                index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive);
            }

            // index is now known to be correct
            res = data.Data[index];

            if (res == Uninitialized) {
                throw new MissingMemberException(klass.GetIndexName(index));
            }

            return res;
        }
示例#12
0
 /// <summary>
 /// Constructs a new ExpandoData object with the specified class and data.
 /// </summary>
 internal ExpandoData(ExpandoClass klass, object[] data) {
     Class = klass;
     Data = data;
 }
示例#13
0
 /// <summary>
 /// Constructs an empty ExpandoData object with the empty class and no data.
 /// </summary>
 private ExpandoData() {
     Class = ExpandoClass.Empty;
     Data = new object[0];
 }
示例#14
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 ignoreCase, out ExpandoClass klass, out int index) {
                ExpandoClass originalClass = Value.Class;

                index = originalClass.GetValueIndex(name, ignoreCase);
                if (index == -1) {
                    // go ahead and find a new class now...
                    ExpandoClass newClass = originalClass.FindNewClass(name, ignoreCase);

                    klass = newClass;
                    index = newClass.GetValueIndex(name, ignoreCase);

                    Debug.Assert(index != -1);
                    return originalClass;
                } else {
                    klass = originalClass;
                    return null;
                }                
            }
示例#15
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 Expression AddDynamicTestAndDefer(MetaObjectBinder binder, MetaObject[] args, ExpandoClass klass, ExpandoClass originalClass, Expression ifTestSucceeds) {
                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),
                            Expression.Constant(klass)
                        ),
                        ifTestSucceeds
                    );
                }

                return Expression.Condition(
                    Expression.Call(
                        null,
                        typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"),
                        GetLimitedSelf(),
                        Expression.Constant(originalClass ?? klass)
                    ),
                    ifTestSucceeds,
                    binder.Defer(args).Expression
                );
            }
示例#16
0
 /// <summary>
 /// Internal helper to promote a class.  Called from our RuntimeOps helper.  This
 /// version simply doesn't expose the ExpandoData object which is a private
 /// data structure.
 /// </summary>
 internal void PromoteClass(ExpandoClass oldClass, ExpandoClass newClass) {
     PromoteClassWorker(oldClass, newClass);
 }
示例#17
0
        /// <summary>
        /// Promotes the class from the old type to the new type and returns the new
        /// ExpandoData object.
        /// </summary>
        private ExpandoData PromoteClassWorker(ExpandoClass oldClass, ExpandoClass newClass) {
            Debug.Assert(oldClass != newClass);

            lock (this) {
                if (_data.Class == oldClass) {
                    _data = new ExpandoData(newClass, newClass.GetNewKeys(_data.Data));
                }
                return _data;
            }
        }
示例#18
0
 /// <summary>
 /// Constructs a new ExpandoData object with the specified class and data.
 /// </summary>
 internal ExpandoData(ExpandoClass klass, object[] data)
 {
     Class = klass;
     Data  = data;
 }
示例#19
0
 /// <summary>
 /// Constructs an empty ExpandoData object with the empty class and no data.
 /// </summary>
 private ExpandoData()
 {
     Class = ExpandoClass.Empty;
     Data  = new object[0];
 }
示例#20
0
        /// <summary>
        /// Gets the data stored for the specified class at the specified index.  If the
        /// class has changed a full lookup for the slot will be performed and the correct
        /// value will be retrieved.
        /// </summary>
        internal bool DeleteValue(ExpandoClass klass, int index, bool caseInsensitive) {
            Debug.Assert(index != -1);

            lock (this) {
                ExpandoData data = _data;

                if (data.Class != klass) {
                    // the class has changed, we need to get the correct index.  If there is
                    // no associated index we simply can't have the value and we return
                    // false.
                    index = data.Class.GetValueIndex(klass.GetIndexName(index), caseInsensitive);
                    if (index == -1) {
                        return false;
                    }
                }

                object curValue = data.Data[index];
                data.Data[index] = Uninitialized;

                return curValue != Uninitialized;
            }
        }
示例#21
0
 /// <summary>
 /// Internal helper to promote a class.  Called from our RuntimeOps helper.  This
 /// version simply doesn't expose the ExpandoData object which is a private
 /// data structure.
 /// </summary>
 internal void PromoteClass(ExpandoClass oldClass, ExpandoClass newClass)
 {
     PromoteClassWorker(oldClass, newClass);
 }
示例#22
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 Expression AddDynamicTestAndDefer(MetaObjectBinder binder, MetaObject[] args, ExpandoClass klass, ExpandoClass originalClass, Expression ifTestSucceeds)
            {
                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),
                            Expression.Constant(klass)
                            ),
                        ifTestSucceeds
                        );
                }

                return(Expression.Condition(
                           Expression.Call(
                               null,
                               typeof(RuntimeOps).GetMethod("ExpandoCheckVersion"),
                               GetLimitedSelf(),
                               Expression.Constant(originalClass ?? klass)
                               ),
                           ifTestSucceeds,
                           binder.Defer(args).Expression
                           ));
            }