4 // Lluis Sanchez Gual (lluis@ideary.com)
6 // (C) 2003 Lluis Sanchez Gual
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
34 using System.Runtime.Serialization;
35 using System.Runtime.Remoting.Messaging;
36 using System.Reflection;
37 using System.Globalization;
39 namespace System.Runtime.Serialization.Formatters.Binary
41 abstract class TypeMetadata
43 public string TypeAssemblyName;
44 public string InstanceTypeName;
46 public abstract void WriteAssemblies (ObjectWriter ow, BinaryWriter writer);
47 public abstract void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes);
48 public abstract void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data);
50 public virtual bool IsCompatible (TypeMetadata other)
55 public abstract bool RequiresTypes { get; }
58 abstract class ClrTypeMetadata: TypeMetadata
60 public Type InstanceType;
62 public ClrTypeMetadata (Type instanceType)
64 InstanceType = instanceType;
65 InstanceTypeName = instanceType.FullName;
66 TypeAssemblyName = instanceType.Assembly.FullName;
69 public override bool RequiresTypes {
74 class SerializableTypeMetadata: TypeMetadata
79 public SerializableTypeMetadata (Type itype, SerializationInfo info)
81 types = new Type [info.MemberCount];
82 names = new string [info.MemberCount];
84 SerializationInfoEnumerator e = info.GetEnumerator ();
89 types[n] = e.ObjectType;
94 TypeAssemblyName = info.AssemblyName;
95 InstanceTypeName = info.FullTypeName;
98 public override bool IsCompatible (TypeMetadata other)
100 if (!(other is SerializableTypeMetadata)) return false;
102 SerializableTypeMetadata tm = (SerializableTypeMetadata)other;
103 if (types.Length != tm.types.Length) return false;
104 if (TypeAssemblyName != tm.TypeAssemblyName) return false;
105 if (InstanceTypeName != tm.InstanceTypeName) return false;
106 for (int n=0; n<types.Length; n++)
108 if (types[n] != tm.types[n]) return false;
109 if (names[n] != tm.names[n]) return false;
114 public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
116 foreach (Type mtype in types)
120 type = type.GetElementType();
122 ow.WriteAssembly (writer, type.Assembly);
126 public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
128 writer.Write (types.Length);
131 foreach (string name in names)
135 foreach (Type type in types)
136 ObjectWriter.WriteTypeCode (writer, type);
138 // Type specs of fields
139 foreach (Type type in types)
140 ow.WriteTypeSpec (writer, type);
143 public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
145 SerializationInfo info = (SerializationInfo) data;
146 SerializationInfoEnumerator e = info.GetEnumerator ();
148 while (e.MoveNext ())
149 ow.WriteValue (writer, e.ObjectType, e.Value);
152 public override bool RequiresTypes {
157 class MemberTypeMetadata: ClrTypeMetadata
159 MemberInfo[] members;
161 public MemberTypeMetadata (Type type, StreamingContext context): base (type)
163 members = FormatterServices.GetSerializableMembers (type, context);
166 public override void WriteAssemblies (ObjectWriter ow, BinaryWriter writer)
168 foreach (FieldInfo field in members)
170 Type type = field.FieldType;
172 type = type.GetElementType();
174 ow.WriteAssembly (writer, type.Assembly);
178 public override void WriteTypeData (ObjectWriter ow, BinaryWriter writer, bool writeTypes)
180 writer.Write (members.Length);
183 foreach (FieldInfo field in members)
184 writer.Write (field.Name);
188 foreach (FieldInfo field in members)
189 ObjectWriter.WriteTypeCode (writer, field.FieldType);
191 // Type specs of fields
192 foreach (FieldInfo field in members)
193 ow.WriteTypeSpec (writer, field.FieldType);
197 public override void WriteObjectData (ObjectWriter ow, BinaryWriter writer, object data)
199 object[] values = FormatterServices.GetObjectData (data, members);
200 for (int n=0; n<values.Length; n++)
201 ow.WriteValue (writer, ((FieldInfo)members[n]).FieldType, values[n]);
205 internal class ObjectWriter
207 ObjectIDGenerator _idGenerator = new ObjectIDGenerator();
208 Hashtable _cachedMetadata = new Hashtable();
209 Queue _pendingObjects = new Queue();
210 Hashtable _assemblyCache = new Hashtable();
212 // Type metadata that can be shared with all serializers
213 static Hashtable _cachedTypes = new Hashtable();
215 internal static Assembly CorlibAssembly = typeof(string).Assembly;
216 internal static string CorlibAssemblyName = typeof(string).Assembly.FullName;
218 ISurrogateSelector _surrogateSelector;
219 StreamingContext _context;
220 FormatterAssemblyStyle _assemblyFormat;
221 FormatterTypeStyle _typeFormat;
223 int ArrayBufferLength = 4096;
224 SerializationObjectManager _manager;
226 class MetadataReference
228 public TypeMetadata Metadata;
229 public long ObjectID;
231 public MetadataReference (TypeMetadata metadata, long id)
238 public ObjectWriter (ISurrogateSelector surrogateSelector, StreamingContext context, FormatterAssemblyStyle assemblyFormat, FormatterTypeStyle typeFormat)
240 _surrogateSelector = surrogateSelector;
242 _assemblyFormat = assemblyFormat;
243 _typeFormat = typeFormat;
244 _manager = new SerializationObjectManager (context);
247 public void WriteObjectGraph (BinaryWriter writer, object obj, Header[] headers)
249 _pendingObjects.Clear();
250 if (headers != null) QueueObject (headers);
252 WriteQueuedObjects (writer);
253 WriteSerializationEnd (writer);
254 _manager.RaiseOnSerializedEvent ();
257 public void QueueObject (object obj)
259 _pendingObjects.Enqueue (obj);
262 public void WriteQueuedObjects (BinaryWriter writer)
264 while (_pendingObjects.Count > 0)
265 WriteObjectInstance (writer, _pendingObjects.Dequeue(), false);
268 public void WriteObjectInstance (BinaryWriter writer, object obj, bool isValueObject)
273 // If the object is a value type (not boxed) then there is no need
274 // to register it in the id generator, because it won't have other
277 if (isValueObject) id = _idGenerator.NextId;
278 else id = _idGenerator.GetId (obj, out firstTime);
281 WriteString (writer, id, (string)obj);
283 else if (obj is Array) {
284 WriteArray (writer, id, (Array)obj);
287 WriteObject (writer, id, obj);
290 public static void WriteSerializationEnd (BinaryWriter writer)
292 writer.Write ((byte) BinaryElement.End);
295 private void WriteObject (BinaryWriter writer, long id, object obj)
298 TypeMetadata metadata;
300 GetObjectData (obj, out metadata, out data);
301 MetadataReference metadataReference = (MetadataReference)_cachedMetadata [metadata.InstanceTypeName];
303 if (metadataReference != null && metadata.IsCompatible (metadataReference.Metadata))
305 // An object of the same type has already been serialized
306 // It is not necessary to write again type metadata
308 writer.Write ((byte) BinaryElement.RefTypeObject);
309 writer.Write ((int)id);
311 writer.Write ((int)metadataReference.ObjectID);
312 metadata.WriteObjectData (this, writer, data);
316 if (metadataReference == null)
318 metadataReference = new MetadataReference (metadata, id);
319 _cachedMetadata [metadata.InstanceTypeName] = metadataReference;
322 bool writeTypes = metadata.RequiresTypes || _typeFormat == FormatterTypeStyle.TypesAlways;
324 BinaryElement objectTag;
327 if (metadata.TypeAssemblyName == CorlibAssemblyName)
330 objectTag = writeTypes ? BinaryElement.RuntimeObject : BinaryElement.UntypedRuntimeObject;
335 objectTag = writeTypes ? BinaryElement.ExternalObject : BinaryElement.UntypedExternalObject;
336 assemblyId = WriteAssemblyName (writer, metadata.TypeAssemblyName);
339 // Registers the assemblies needed for each field
340 // If there are assemblies that where not registered before this object,
343 metadata.WriteAssemblies (this, writer);
347 writer.Write ((byte) objectTag);
348 writer.Write ((int)id);
349 writer.Write (metadata.InstanceTypeName);
351 metadata.WriteTypeData (this, writer, writeTypes);
352 if (assemblyId != -1) writer.Write (assemblyId);
354 metadata.WriteObjectData (this, writer, data);
357 private void GetObjectData (object obj, out TypeMetadata metadata, out object data)
359 Type instanceType = obj.GetType();
361 // Check if the formatter has a surrogate selector, if it does,
362 // check if the surrogate selector handles objects of the given type.
364 if (_surrogateSelector != null)
366 ISurrogateSelector selector;
367 ISerializationSurrogate surrogate = _surrogateSelector.GetSurrogate (instanceType, _context, out selector);
368 if (surrogate != null)
370 SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
371 surrogate.GetObjectData (obj, info, _context);
372 metadata = new SerializableTypeMetadata (instanceType, info);
378 // Check if the object is marked with the Serializable attribute
380 BinaryCommon.CheckSerializable (instanceType, _surrogateSelector, _context);
382 _manager.RegisterObject (obj);
384 ISerializable ser = obj as ISerializable;
388 SerializationInfo info = new SerializationInfo (instanceType, new FormatterConverter ());
389 ser.GetObjectData (info, _context);
390 metadata = new SerializableTypeMetadata (instanceType, info);
396 if (_context.Context != null)
398 // Don't cache metadata info when the Context property is not null sice
399 // we can't control the number of possible contexts in this case
400 metadata = new MemberTypeMetadata (instanceType, _context);
404 Hashtable typesTable;
406 lock (_cachedTypes) {
407 typesTable = (Hashtable) _cachedTypes [_context.State];
408 if (typesTable == null) {
409 typesTable = new Hashtable ();
410 _cachedTypes [_context.State] = typesTable;
418 metadata = (TypeMetadata) typesTable [instanceType];
421 if (metadata == null) {
422 metadata = CreateMemberTypeMetadata (instanceType);
425 typesTable [instanceType] = metadata;
430 TypeMetadata CreateMemberTypeMetadata (Type type)
432 if (!BinaryCommon.UseReflectionSerialization) {
433 Type metaType = CodeGenerator.GenerateMetadataType (type, _context);
434 return (TypeMetadata) Activator.CreateInstance (metaType);
437 return new MemberTypeMetadata (type, _context);
440 private void WriteArray (BinaryWriter writer, long id, Array array)
442 // There are 4 ways of serializing arrays:
443 // The element GenericArray (7) can be used for all arrays.
444 // The element ArrayOfPrimitiveType (15) can be used for single-dimensional
445 // arrays of primitive types
446 // The element ArrayOfObject (16) can be used for single-dimensional Object arrays
447 // The element ArrayOfString (17) can be used for single-dimensional string arrays
449 Type elementType = array.GetType().GetElementType();
451 if (elementType == typeof (object) && array.Rank == 1) {
452 WriteObjectArray (writer, id, array);
454 else if (elementType == typeof (string) && array.Rank == 1) {
455 WriteStringArray (writer, id, array);
457 else if (BinaryCommon.IsPrimitive(elementType) && array.Rank == 1) {
458 WritePrimitiveTypeArray (writer, id, array);
461 WriteGenericArray (writer, id, array);
464 private void WriteGenericArray (BinaryWriter writer, long id, Array array)
466 Type elementType = array.GetType().GetElementType();
468 // Registers and writes the assembly of the array element type if needed
470 if (!elementType.IsArray)
471 WriteAssembly (writer, elementType.Assembly);
475 writer.Write ((byte) BinaryElement.GenericArray);
476 writer.Write ((int)id);
478 // Write the structure of the array
480 if (elementType.IsArray)
481 writer.Write ((byte) ArrayStructure.Jagged);
482 else if (array.Rank == 1)
483 writer.Write ((byte) ArrayStructure.SingleDimensional);
485 writer.Write ((byte) ArrayStructure.MultiDimensional);
487 // Write the number of dimensions and the length
490 writer.Write (array.Rank);
491 for (int n=0; n<array.Rank; n++)
492 writer.Write (array.GetUpperBound (n) + 1);
495 WriteTypeCode (writer, elementType);
496 WriteTypeSpec (writer, elementType);
498 // Writes the values. For single-dimension array, a special tag is used
499 // to represent multiple consecutive null values. I don't know why this
500 // optimization is not used for multidimensional arrays.
502 if (array.Rank == 1 && !elementType.IsValueType)
504 WriteSingleDimensionArrayElements (writer, array, elementType);
508 foreach (object item in array)
509 WriteValue (writer, elementType, item);
513 private void WriteObjectArray (BinaryWriter writer, long id, Array array)
515 writer.Write ((byte) BinaryElement.ArrayOfObject);
516 writer.Write ((int)id);
517 writer.Write (array.Length); // Single dimension. Just write the length
518 WriteSingleDimensionArrayElements (writer, array, typeof (object));
521 private void WriteStringArray (BinaryWriter writer, long id, Array array)
523 writer.Write ((byte) BinaryElement.ArrayOfString);
524 writer.Write ((int)id);
525 writer.Write (array.Length); // Single dimension. Just write the length
526 WriteSingleDimensionArrayElements (writer, array, typeof (string));
529 private void WritePrimitiveTypeArray (BinaryWriter writer, long id, Array array)
531 writer.Write ((byte) BinaryElement.ArrayOfPrimitiveType);
532 writer.Write ((int)id);
533 writer.Write (array.Length); // Single dimension. Just write the length
535 Type elementType = array.GetType().GetElementType();
536 WriteTypeSpec (writer, elementType);
538 switch (Type.GetTypeCode (elementType))
540 case TypeCode.Boolean:
541 foreach (bool item in (bool[]) array)
546 writer.Write ((byte[]) array);
550 writer.Write ((char[]) array);
553 case TypeCode.DateTime:
554 foreach (DateTime item in (DateTime[]) array)
555 writer.Write (item.ToBinary ());
558 case TypeCode.Decimal:
559 foreach (decimal item in (decimal[]) array)
563 case TypeCode.Double:
564 if (array.Length > 2)
565 BlockWrite (writer, array, 8);
567 foreach (double item in (double[]) array)
572 if (array.Length > 2)
573 BlockWrite (writer, array, 2);
575 foreach (short item in (short[]) array)
580 if (array.Length > 2)
581 BlockWrite (writer, array, 4);
583 foreach (int item in (int[]) array)
588 if (array.Length > 2)
589 BlockWrite (writer, array, 8);
591 foreach (long item in (long[]) array)
596 if (array.Length > 2)
597 BlockWrite (writer, array, 1);
599 foreach (sbyte item in (sbyte[]) array)
603 case TypeCode.Single:
604 if (array.Length > 2)
605 BlockWrite (writer, array, 4);
607 foreach (float item in (float[]) array)
611 case TypeCode.UInt16:
612 if (array.Length > 2)
613 BlockWrite (writer, array, 2);
615 foreach (ushort item in (ushort[]) array)
619 case TypeCode.UInt32:
620 if (array.Length > 2)
621 BlockWrite (writer, array, 4);
623 foreach (uint item in (uint[]) array)
627 case TypeCode.UInt64:
628 if (array.Length > 2)
629 BlockWrite (writer, array, 8);
631 foreach (ulong item in (ulong[]) array)
635 case TypeCode.String:
636 foreach (string item in (string[]) array)
641 if (elementType == typeof (TimeSpan)) {
642 foreach (TimeSpan item in (TimeSpan[]) array)
643 writer.Write (item.Ticks);
646 throw new NotSupportedException ("Unsupported primitive type: " + elementType.FullName);
651 private void BlockWrite (BinaryWriter writer, Array array, int dataSize)
653 int totalSize = Buffer.ByteLength (array);
655 if (arrayBuffer == null || (totalSize > arrayBuffer.Length && arrayBuffer.Length != ArrayBufferLength))
656 arrayBuffer = new byte [totalSize <= ArrayBufferLength ? totalSize : ArrayBufferLength];
659 while (totalSize > 0) {
660 int size = totalSize < arrayBuffer.Length ? totalSize : arrayBuffer.Length;
661 Buffer.BlockCopy (array, pos, arrayBuffer, 0, size);
663 if (!BitConverter.IsLittleEndian && dataSize > 1)
664 BinaryCommon.SwapBytes (arrayBuffer, size, dataSize);
666 writer.Write (arrayBuffer, 0, size);
672 private void WriteSingleDimensionArrayElements (BinaryWriter writer, Array array, Type elementType)
675 foreach (object val in array)
677 if (val != null && numNulls > 0)
679 WriteNullFiller (writer, numNulls);
680 WriteValue (writer, elementType, val);
683 else if (val == null)
686 WriteValue (writer, elementType, val);
689 WriteNullFiller (writer, numNulls);
692 private void WriteNullFiller (BinaryWriter writer, int numNulls)
695 writer.Write ((byte) BinaryElement.NullValue);
697 else if (numNulls == 2) {
698 writer.Write ((byte) BinaryElement.NullValue);
699 writer.Write ((byte) BinaryElement.NullValue);
701 else if (numNulls <= byte.MaxValue) {
702 writer.Write ((byte) BinaryElement.ArrayFiller8b);
703 writer.Write ((byte) numNulls);
706 writer.Write ((byte) BinaryElement.ArrayFiller32b);
707 writer.Write (numNulls);
711 private void WriteObjectReference (BinaryWriter writer, long id)
714 writer.Write ((byte) BinaryElement.ObjectReference);
715 writer.Write ((int)id);
718 public void WriteValue (BinaryWriter writer, Type valueType, object val)
722 BinaryCommon.CheckSerializable (valueType, _surrogateSelector, _context);
723 writer.Write ((byte) BinaryElement.NullValue);
725 else if (BinaryCommon.IsPrimitive(val.GetType()))
727 if (!BinaryCommon.IsPrimitive(valueType))
729 // It is a boxed primitive type value
730 writer.Write ((byte) BinaryElement.BoxedPrimitiveTypeValue);
731 WriteTypeSpec (writer, val.GetType());
733 WritePrimitiveValue (writer, val);
735 else if (valueType.IsValueType)
737 // Value types are written embedded in the containing object
738 WriteObjectInstance (writer, val, true);
740 else if (val is string)
742 // Strings are written embedded, unless already registered
744 long id = _idGenerator.GetId (val, out firstTime);
746 if (firstTime) WriteObjectInstance (writer, val, false);
747 else WriteObjectReference (writer, id);
751 // It is a reference type. Write a forward reference and queue the
752 // object to the pending object list (unless already written).
755 long id = _idGenerator.GetId (val, out firstTime);
757 if (firstTime) _pendingObjects.Enqueue (val);
758 WriteObjectReference (writer, id);
762 private void WriteString (BinaryWriter writer, long id, string str)
764 writer.Write ((byte) BinaryElement.String);
765 writer.Write ((int)id);
769 public int WriteAssembly (BinaryWriter writer, Assembly assembly)
771 return WriteAssemblyName (writer, assembly.FullName);
774 public int WriteAssemblyName (BinaryWriter writer, string assembly)
776 if (assembly == ObjectWriter.CorlibAssemblyName) return -1;
779 int id = RegisterAssembly (assembly, out firstTime);
780 if (!firstTime) return id;
782 writer.Write ((byte) BinaryElement.Assembly);
784 if (_assemblyFormat == FormatterAssemblyStyle.Full)
785 writer.Write (assembly);
787 int i = assembly.IndexOf (',');
788 if (i != -1) assembly = assembly.Substring (0, i);
789 writer.Write (assembly);
795 public int GetAssemblyId (Assembly assembly)
797 return GetAssemblyNameId (assembly.FullName);
800 public int GetAssemblyNameId (string assembly)
802 return (int)_assemblyCache[assembly];
805 private int RegisterAssembly (string assembly, out bool firstTime)
807 if (_assemblyCache.ContainsKey (assembly))
810 return (int)_assemblyCache[assembly];
814 int id = (int)_idGenerator.GetId (0, out firstTime);
815 _assemblyCache.Add (assembly, id);
820 public static void WritePrimitiveValue (BinaryWriter writer, object value)
822 Type type = value.GetType();
824 switch (Type.GetTypeCode (type))
826 case TypeCode.Boolean:
827 writer.Write ((bool)value);
831 writer.Write ((byte) value);
835 writer.Write ((char) value);
838 case TypeCode.DateTime:
839 writer.Write ( ((DateTime)value).ToBinary ());
842 case TypeCode.Decimal:
843 writer.Write (((decimal) value).ToString (CultureInfo.InvariantCulture));
846 case TypeCode.Double:
847 writer.Write ((double) value);
851 writer.Write ((short) value);
855 writer.Write ((int) value);
859 writer.Write ((long) value);
863 writer.Write ((sbyte) value);
866 case TypeCode.Single:
867 writer.Write ((float) value);
870 case TypeCode.UInt16:
871 writer.Write ((ushort) value);
874 case TypeCode.UInt32:
875 writer.Write ((uint) value);
878 case TypeCode.UInt64:
879 writer.Write ((ulong) value);
882 case TypeCode.String:
883 writer.Write ((string) value);
887 if (type == typeof (TimeSpan))
888 writer.Write (((TimeSpan)value).Ticks);
890 throw new NotSupportedException ("Unsupported primitive type: " + value.GetType().FullName);
895 public static void WriteTypeCode (BinaryWriter writer, Type type)
897 writer.Write ((byte) GetTypeTag (type));
900 public static TypeTag GetTypeTag (Type type)
902 if (type == typeof (string)) {
903 return TypeTag.String;
905 else if (BinaryCommon.IsPrimitive (type)) {
906 return TypeTag.PrimitiveType;
908 else if (type == typeof (object)) {
909 return TypeTag.ObjectType;
911 else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (object)) {
912 return TypeTag.ArrayOfObject;
914 else if (type.IsArray && type.GetArrayRank() == 1 && type.GetElementType() == typeof (string)){
915 return TypeTag.ArrayOfString;
917 else if (type.IsArray && type.GetArrayRank() == 1 && BinaryCommon.IsPrimitive(type.GetElementType())) {
918 return TypeTag.ArrayOfPrimitiveType;
920 else if (type.Assembly == CorlibAssembly) {
921 return TypeTag.RuntimeType;
924 return TypeTag.GenericType;
927 public void WriteTypeSpec (BinaryWriter writer, Type type)
929 // WARNING Keep in sync with EmitWriteTypeSpec
931 switch (GetTypeTag (type))
933 case TypeTag.PrimitiveType:
934 writer.Write (BinaryCommon.GetTypeCode (type));
937 case TypeTag.RuntimeType:
938 string fullName = type.FullName;
939 // Map System.MonoType to MS.NET's System.RuntimeType,
940 // when called in remoting context.
941 // Note that this code does not need to be in sync with
942 // EmitWriteTypeSpec because serializing a MethodCall
943 // won't trigger the CodeGenerator.
944 if (_context.State == StreamingContextStates.Remoting)
945 if (type == typeof (System.MonoType))
946 fullName = "System.RuntimeType";
947 else if (type == typeof (System.MonoType[]))
948 fullName = "System.RuntimeType[]";
949 writer.Write (fullName);
952 case TypeTag.GenericType:
953 writer.Write (type.FullName);
954 writer.Write ((int)GetAssemblyId (type.Assembly));
957 case TypeTag.ArrayOfPrimitiveType:
958 writer.Write (BinaryCommon.GetTypeCode (type.GetElementType()));
962 // Type spec not needed