edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs;C537338
File: HashOps.cs
===================================================================
--- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs;C537338  (server)    9/5/2008 5:07 PM
+++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/HashOps.cs;InfiniteRubyRecursion1
@@ -126,11 +126,11 @@
 
         [RubyMethod("inspect")]
         public static MutableString/*!*/ Inspect(CodeContext/*!*/ context, Hash/*!*/ self) {
-            Dictionary<object, bool> infinite = RubyUtils.TryPushInfinite(self);
-            if (infinite == null) {
-                return MutableString.Create("{...}");
-            }
-            try {
+            using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
+                if (handle == null) {
+                    return MutableString.Create("{...}");
+                }
+
                 MutableString str = MutableString.Create("{");
                 bool first = true;
                 foreach (KeyValuePair<object, object> pair in self) {
@@ -145,8 +145,6 @@
                 }
                 str.Append('}');
                 return str;
-            } finally {
-                infinite.Remove(self);
             }
         }
 
===================================================================
edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Struct.cs;C537338
File: Struct.cs
===================================================================
--- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Struct.cs;C537338  (server)    9/8/2008 8:51 AM
+++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Builtins/Struct.cs;InfiniteRubyRecursion1
@@ -290,16 +290,15 @@
         [RubyMethod("to_s")]
         [RubyMethod("inspect")]
         public static MutableString/*!*/ Inspect(CodeContext/*!*/ context, RubyStruct/*!*/ self) {
-            RubyExecutionContext ec = RubyUtils.GetExecutionContext(context);
-            // #<struct Struct::Foo name=nil, val=nil>
-            MutableString str = MutableString.Create("#<struct ");
-            str.Append(RubySites.Inspect(context, ec.GetClassOf(self)));
+            using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
+                RubyExecutionContext ec = RubyUtils.GetExecutionContext(context);
+                // #<struct Struct::Foo name=nil, val=nil>
+                MutableString str = MutableString.Create("#<struct ");
+                str.Append(RubySites.Inspect(context, ec.GetClassOf(self)));
 
-            Dictionary<object, bool> infinite = RubyUtils.TryPushInfinite(self);
-            if (infinite == null) {
-                return str.Append(":...>");
-            }
-            try {
+                if (handle == null) {
+                    return str.Append(":...>");
+                }
                 str.Append(' ');
 
                 object[] data = self._data;
@@ -314,8 +313,6 @@
                 }
                 str.Append('>');
                 return str;
-            } finally {
-                infinite.Remove(self);
             }
         }
 
===================================================================
edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs;C533798
File: IDictionaryOps.cs
===================================================================
--- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs;C533798  (server)    9/8/2008 9:03 AM
+++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IDictionaryOps.cs;InfiniteRubyRecursion1
@@ -329,11 +329,10 @@
 
         [RubyMethod("inspect")]
         public static MutableString Inspect(CodeContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
-            Dictionary<object, bool> infinite = RubyUtils.TryPushInfinite(self);
-            if (infinite == null) {
-                return MutableString.Create("{...}");
-            }
-            try {
+            using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
+                if (handle == null) {
+                    return MutableString.Create("{...}");
+                }
                 MutableString str = MutableString.CreateMutable();
                 str.Append('{');
                 foreach (KeyValuePair<object, object> pair in self) {
@@ -346,8 +345,6 @@
                 }
                 str.Append('}');
                 return str;
-            } finally {
-                infinite.Remove(self);
             }
         }
 
@@ -514,7 +511,13 @@
 
         [RubyMethod("to_s")]
         public static MutableString ToString(CodeContext/*!*/ context, IDictionary<object, object>/*!*/ self) {
-            return IListOps.Join(context, ToArray(self));
+            using (IDisposable handle = RubyUtils.InfiniteToSTracker.TrackObject(self)) {
+                if (handle == null) {
+                    return MutableString.Create("{...}");
+                } else {
+                    return IListOps.Join(context, ToArray(self));
+                }
+            }
         }
 
         [RubyMethod("values")]
===================================================================
edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs;C538514
File: IListOps.cs
===================================================================
--- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs;C538514  (server)    9/5/2008 4:34 PM
+++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Libraries.LCA_RESTRICTED/Extensions/IListOps.cs;InfiniteRubyRecursion1
@@ -19,6 +19,7 @@
 using System.Diagnostics;
 using System.Reflection;
 using System.Runtime.InteropServices;
+using Microsoft.Scripting;
 using Microsoft.Scripting.Runtime;
 using Microsoft.Scripting.Utils;
 using Microsoft.Scripting.Actions;
@@ -926,43 +927,38 @@
 
         #region flatten, flatten!
 
-        public static bool TryRecursiveFlatten(CodeContext/*!*/ context, IList list, IList result, Dictionary<object, bool> seen) {
-            bool flattened = false;
-            for (int i = 0; i < list.Count; ++i) {
-                IList item = list[i] as IList;
-                if (item == null) {
-                    item = Protocols.AsArray(context, list[i]);
+        [MultiRuntimeAware]
+        private static RubyUtils.RecursionTracker _infiniteFlattenTracker = new RubyUtils.RecursionTracker();
+
+        public static bool TryFlattenArray(CodeContext/*!*/ context, IList list, out IList/*!*/ result) {
+            // TODO: create correct subclass of RubyArray rather than RubyArray directly
+            result = new RubyArray();
+
+            using (IDisposable handle = _infiniteFlattenTracker.TrackObject(list)) {
+                if (handle == null) {
+                    throw RubyExceptions.CreateArgumentError("tried to flatten recursive array");
                 }
+                bool flattened = false;
+                for (int i = 0; i < list.Count; ++i) {
+                    IList item = list[i] as IList;
+                    if (item == null) {
+                        item = Protocols.AsArray(context, list[i]);
+                    }
 
-                if (item == null) {
-                    result.Add(list[i]);
-                } else {
-                    flattened = true;
-                    if (!seen.ContainsKey(item)) {
-                        seen.Add(item, true);
-
-                        // TODO: should call via a site to do this, but we need a way of flowing the seen dictionary
-                        // across the boundary - maybe use a thread static slot to store the dictionary in the future?
-                        // IList flattenedItem = LibrarySites.InvokeFlatten(context, item);
-                        // if (flattenedItem != null) {
-                        //     AddRange(result, flattenedItem);
-                        // }
-                        TryRecursiveFlatten(context, item, result, seen);
-                    } else
-                        throw RubyExceptions.CreateArgumentError("tried to flatten recursive array");
+                    if (item == null) {
+                        result.Add(list[i]);
+                    } else {
+                        flattened = true;
+                        IList flattenedItem = LibrarySites.InvokeFlatten(context, item);
+                        if (flattenedItem != null) {
+                            AddRange(result, flattenedItem);
+                        }
+                    }
                 }
+                return flattened;
             }
-            return flattened;
         }
 
-        public static bool TryFlattenArray(CodeContext/*!*/ context, IList list, out IList/*!*/ result) {
-            Dictionary<object, bool> seen = new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance);
-            seen.Add(list, true);
-            // TODO: create correct subclass of RubyArray rather than RubyArray directly
-            result = new RubyArray();
-            return TryRecursiveFlatten(context, list, result, seen);
-        }
-
         [RubyMethod("flatten")]
         public static IList/*!*/ Flatten(CodeContext/*!*/ context, IList/*!*/ self) {
             IList result;
@@ -1061,11 +1057,10 @@
 
         [RubyMethod("inspect")]
         public static MutableString Inspect(CodeContext/*!*/ context, IList/*!*/ self) {
-            Dictionary<object, bool> infinite = RubyUtils.TryPushInfinite(self);
-            if (infinite == null) {
-                return MutableString.Create("[...]");
-            }
-            try {
+            using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
+                if (handle == null) {
+                    return MutableString.Create("[...]");
+                }
                 MutableString str = MutableString.CreateMutable();
                 str.Append('[');
                 foreach (object obj in self) {
@@ -1076,8 +1071,6 @@
                 }
                 str.Append(']');
                 return str;
-            } finally {
-                infinite.Remove(self);
             }
         }
 
@@ -1094,7 +1087,7 @@
             }
 
             seen.Add(list, true); // push
-            
+
             for (int i = 0; i < list.Count; ++i) {
                 object item = list[i];
 
@@ -1122,14 +1115,14 @@
         [RubyMethod("join")]
         [RubyMethod("to_s")]
         public static MutableString/*!*/ Join(CodeContext/*!*/ context, [NotNull]IList/*!*/ self) {
-            Assert.NotNull(context, self);
-
             MutableString separator = RubyUtils.GetExecutionContext(context).ItemSeparator;
             return Join(context, self, (separator != null ? separator.ConvertToString() : String.Empty));
         }
 
         [RubyMethod("join")]
         public static MutableString Join(CodeContext/*!*/ context, IList/*!*/ self, string separator) {
+            Assert.NotNull(context, self, separator);
+
             MutableString result = MutableString.CreateMutable();
             RecursiveJoin(context, self, separator, result, new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance));
             return result;
===================================================================
edit: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C540639
File: RubyUtils.cs
===================================================================
--- $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;C540639  (server)    9/5/2008 4:57 PM
+++ Shelved Change: $/Dev10/feature/vs_langs01/Merlin/Main/Languages/Ruby/Ruby/Runtime/RubyUtils.cs;InfiniteRubyRecursion1
@@ -205,51 +205,52 @@
             return true;
         }
 
+        [MultiRuntimeAware]
+        private static RecursionTracker/*!*/ _infiniteCopyTracker = new RecursionTracker();
+
         public static object DeepCopy(CodeContext/*!*/ context, object obj) {
-            Dictionary<object, bool> infinite = RubyUtils.TryPushInfinite(obj);
-            if (infinite == null) {
-                return RubyExceptions.CreateArgumentError("unable to deep copy recursive structure");
-            }
-            try {
-                RubyExecutionContext ec = RubyUtils.GetExecutionContext(context);
+            using (IDisposable handle = _infiniteCopyTracker.TrackObject(obj)) {
+                if (handle == null) {
+                    return RubyExceptions.CreateArgumentError("unable to deep copy recursive structure");
+                } else {
+                    RubyExecutionContext ec = RubyUtils.GetExecutionContext(context);
 
-                if (RubyUtils.IsRubyValueType(obj)) {
-                    return obj;
-                }
+                    if (RubyUtils.IsRubyValueType(obj)) {
+                        return obj;
+                    }
 
-                object copy;
+                    object copy;
 
-                // TODO: special case class objects:
-                RubyClass classObject = obj as RubyClass;
-                if (classObject != null) {
-                    copy = classObject.Duplicate();
-                } else {
-                    copy = RubySites.Allocate(context, ec.GetClassOf(obj));
-                }
+                    // TODO: special case class objects:
+                    RubyClass classObject = obj as RubyClass;
+                    if (classObject != null) {
+                        copy = classObject.Duplicate();
+                    } else {
+                        copy = RubySites.Allocate(context, ec.GetClassOf(obj));
+                    }
 
-                SymbolId[] names = ec.GetInstanceVariableNames(obj);
-                RubyInstanceData newVars = (names.Length > 0) ? ec.GetInstanceData(copy) : null;
-                foreach (SymbolId name in names) {
-                    object value;
-                    if (!ec.TryGetInstanceVariable(obj, name, out value)) {
-                        value = null;
-                    } else {
-                        value = DeepCopy(context, value);
+                    SymbolId[] names = ec.GetInstanceVariableNames(obj);
+                    RubyInstanceData newVars = (names.Length > 0) ? ec.GetInstanceData(copy) : null;
+                    foreach (SymbolId name in names) {
+                        object value;
+                        if (!ec.TryGetInstanceVariable(obj, name, out value)) {
+                            value = null;
+                        } else {
+                            value = DeepCopy(context, value);
+                        }
+                        newVars.SetInstanceVariable(name, value);
                     }
-                    newVars.SetInstanceVariable(name, value);
-                }
 
-                if (classObject == null) {
-                    // do any special copying needed for library types
-                    // TODO: we still need to implement copy semantics for .NET types in general
-                    IDuplicable duplicable = copy as IDuplicable;
-                    if (duplicable != null) {
-                        duplicable.InitializeFrom(obj);
+                    if (classObject == null) {
+                        // do any special copying needed for library types
+                        // TODO: we still need to implement copy semantics for .NET types in general
+                        IDuplicable duplicable = copy as IDuplicable;
+                        if (duplicable != null) {
+                            duplicable.InitializeFrom(obj);
+                        }
                     }
+                    return copy;
                 }
-                return copy;
-            } finally {
-                infinite.Remove(obj);
             }
         }
 
@@ -468,23 +469,59 @@
             return module;
         }
 
-        #region Tracking infinite inspect
+        #region Tracking operations that have the potential for infinite recursion
 
-        [ThreadStatic]
-        private static Dictionary<object, bool> _infiniteTracker;
+        public class RecursionTracker {
+            [ThreadStatic]
+            private Dictionary<object, bool> _infiniteTracker;
 
-        public static Dictionary<object, bool> TryPushInfinite(object obj) {
-            if (_infiniteTracker == null) {
-                _infiniteTracker = new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance);
+            private Dictionary<object, bool> TryPushInfinite(object obj) {
+                if (_infiniteTracker == null) {
+                    _infiniteTracker = new Dictionary<object, bool>(ReferenceEqualityComparer<object>.Instance);
+                }
+                Dictionary<object, bool> infinite = _infiniteTracker;
+                if (infinite.ContainsKey(obj)) {
+                    return null;
+                }
+                infinite.Add(obj, true);
+                return infinite;
             }
-            Dictionary<object, bool> infinite = _infiniteTracker;
-            if (infinite.ContainsKey(obj)) {
-                return null;
+
+            public IDisposable TrackObject(object obj) {
+                obj = BaseSymbolDictionary.NullToObj(obj);
+                Dictionary<object, bool> tracker = TryPushInfinite(obj);
+                return (tracker == null) ? null : new RecursionHandle(tracker, obj);
             }
-            infinite.Add(obj, true);
-            return infinite;
+
+            private class RecursionHandle : IDisposable {
+                private readonly Dictionary<object, bool>/*!*/ _tracker;
+                private readonly object _obj;
+
+                internal RecursionHandle(Dictionary<object, bool>/*!*/ tracker, object obj) {
+                    _tracker = tracker;
+                    _obj = obj;
+                }
+
+                public void Dispose() {
+                    _tracker.Remove(_obj);
+                }
+            }
         }
 
+        [MultiRuntimeAware]
+        private static readonly RecursionTracker _infiniteInspectTracker = new RecursionTracker();
+
+        public static RecursionTracker InfiniteInspectTracker {
+            get { return _infiniteInspectTracker; }
+        }
+
+        [MultiRuntimeAware]
+        private static readonly RecursionTracker _infiniteToSTracker = new RecursionTracker();
+
+        public static RecursionTracker InfiniteToSTracker {
+            get { return _infiniteToSTracker; }
+        }
+
         #endregion
 
         // MRI checks for a subtype of RubyArray of subtypes of MutableString.
===================================================================
