private void WriteAnObject(object obj) { if (_recursionLimit == 0) { throw RubyExceptions.CreateArgumentError("exceed depth limit"); } if (_recursionLimit > 0) { _recursionLimit--; } if (obj is int) { int value = (int)obj; if (value < -(1 << 30) || value >= (1 << 30)) { obj = (BigInteger)value; } } // TODO: use RubyUtils.IsRubyValueType? RubySymbol sym; if (obj == null) { _writer.Write((byte)'0'); } else if (obj is bool) { _writer.Write((byte)((bool)obj ? 'T' : 'F')); } else if (obj is int) { WriteFixnum((int)obj); } else if ((sym = obj as RubySymbol) != null) { // TODO (encoding): WriteSymbol(sym.ToString(), sym.Encoding); } else { int objectRef; if (_objects.TryGetValue(obj, out objectRef)) { _writer.Write((byte)'@'); WriteInt32(objectRef); } else { objectRef = _objects.Count; _objects[obj] = objectRef; // TODO: replace with a table-driven implementation // TODO: visibility? bool implementsDump = _context.ResolveMethod(obj, "_dump", VisibilityContext.AllVisible).Found; bool implementsMarshalDump = _context.ResolveMethod(obj, "marshal_dump", VisibilityContext.AllVisible).Found; bool writeInstanceData = false; string[] instanceNames = null; if (!implementsDump && !implementsMarshalDump) { // Neither "_dump" nor "marshal_dump" writes instance vars separately instanceNames = _context.GetInstanceVariableNames(obj); if (instanceNames.Length > 0) { _writer.Write((byte)'I'); writeInstanceData = true; } } if (!implementsDump || implementsMarshalDump) { // "_dump" doesn't write "extend" info but "marshal_dump" does RubyClass theClass = _context.GetImmediateClassOf(obj); if (theClass.IsSingletonClass) { foreach (var mixin in theClass.GetMixins()) { _writer.Write((byte)'e'); WriteModuleName(mixin); } } } if (obj is double) { WriteFloat((double)obj); } else if (obj is float) { WriteFloat((double)(float)obj); } else if (obj is BigInteger) { WriteBignum((BigInteger)obj); } else if (implementsMarshalDump) { WriteUsingMarshalDump(obj); } else if (implementsDump) { WriteUsingDump(obj); } else if (obj is MutableString) { WriteString((MutableString)obj); } else if (obj is RubyArray) { WriteArray((RubyArray)obj); } else if (obj is Hash) { WriteHash((Hash)obj); } else if (obj is RubyRegex) { WriteRegex((RubyRegex)obj); } else if (obj is RubyClass) { WriteClass((RubyClass)obj); } else if (obj is RubyModule) { WriteModule((RubyModule)obj); } else if (obj is RubyStruct) { WriteStruct((RubyStruct)obj); } else if (obj is Range) { WriteRange((Range)obj); } else { if (writeInstanceData) { // Overwrite the "I"; we always have instance data _writer.BaseStream.Seek(-1, SeekOrigin.Current); } else { writeInstanceData = true; } WriteObject(obj); } if (writeInstanceData) { WriteInt32(instanceNames.Length); var encoding = _context.GetIdentifierEncoding(); foreach (string name in instanceNames) { object value; if (!_context.TryGetInstanceVariable(obj, name, out value)) { value = null; } // TODO (encoding): WriteSymbol(name, encoding); WriteAnObject(value); } } } } if (_recursionLimit >= 0) { _recursionLimit++; } }