New test.
[mono.git] / mcs / class / corlib / System.Runtime.Serialization.Formatters.Binary / MessageFormatter.cs
1 //\r
2 // System.Runtime.Remoting.MessageFormatter.cs\r
3 //\r
4 // Author: Lluis Sanchez Gual (lluis@ideary.com)\r
5 //\r
6 // (C) 2003, Lluis Sanchez Gual\r
7 //\r
8 \r
9 //\r
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)\r
11 //\r
12 // Permission is hereby granted, free of charge, to any person obtaining\r
13 // a copy of this software and associated documentation files (the\r
14 // "Software"), to deal in the Software without restriction, including\r
15 // without limitation the rights to use, copy, modify, merge, publish,\r
16 // distribute, sublicense, and/or sell copies of the Software, and to\r
17 // permit persons to whom the Software is furnished to do so, subject to\r
18 // the following conditions:\r
19 // \r
20 // The above copyright notice and this permission notice shall be\r
21 // included in all copies or substantial portions of the Software.\r
22 // \r
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
30 //\r
31 \r
32 using System;\r
33 using System.IO;\r
34 using System.Reflection;\r
35 using System.Collections;\r
36 using System.Runtime.Remoting;\r
37 using System.Runtime.Serialization;\r
38 using System.Runtime.Remoting.Messaging;\r
39 \r
40 namespace System.Runtime.Serialization.Formatters.Binary\r
41 {\r
42         internal class MessageFormatter\r
43         {\r
44                 public static void WriteMethodCall (BinaryWriter writer, object obj, Header[] headers, ISurrogateSelector surrogateSelector, StreamingContext context, FormatterAssemblyStyle assemblyFormat, FormatterTypeStyle typeFormat)\r
45                 {\r
46                         IMethodCallMessage call = (IMethodCallMessage)obj;\r
47                         writer.Write ((byte) BinaryElement.MethodCall);\r
48 \r
49                         MethodFlags methodFlags;\r
50                         int infoArraySize = 0;\r
51                         object info = null;\r
52                         object[] extraProperties = null;\r
53 \r
54                         if (call.LogicalCallContext != null && call.LogicalCallContext.HasInfo)\r
55                         {\r
56                                 methodFlags = MethodFlags.IncludesLogicalCallContext;\r
57                                 infoArraySize++;\r
58                         }\r
59                         else\r
60                                 methodFlags = MethodFlags.ExcludeLogicalCallContext;\r
61 \r
62                         if (RemotingServices.IsMethodOverloaded (call))\r
63                         {\r
64                                 infoArraySize++;\r
65                                 methodFlags |= MethodFlags.IncludesSignature;\r
66                         }\r
67 \r
68                         if (call.Properties.Count > MethodCallDictionary.InternalKeys.Length)\r
69                         {\r
70                                 extraProperties = GetExtraProperties (call.Properties, MethodCallDictionary.InternalKeys);\r
71                                 infoArraySize++;\r
72                         }\r
73 \r
74                         if (call.ArgCount == 0)\r
75                                 methodFlags |= MethodFlags.NoArguments;\r
76                         else {\r
77                                 if (AllTypesArePrimitive (call.Args)) \r
78                                         methodFlags |= MethodFlags.PrimitiveArguments;\r
79                                 else {\r
80                                         if (infoArraySize == 0)\r
81                                                 methodFlags |= MethodFlags.ArgumentsInSimpleArray;\r
82                                         else {\r
83                                                 methodFlags |= MethodFlags.ArgumentsInMultiArray;\r
84                                                 infoArraySize++;\r
85                                         }\r
86                                 }\r
87                         }\r
88 \r
89                         writer.Write ((byte) (methodFlags));\r
90 \r
91                         // FIXME: what are the following 3 bytes for?\r
92                         writer.Write ((byte) 0);\r
93                         writer.Write ((byte) 0);\r
94                         writer.Write ((byte) 0);\r
95 \r
96                         // Method name\r
97                         writer.Write ((byte) BinaryTypeCode.String);\r
98                         writer.Write (call.MethodName);\r
99 \r
100                         // Class name\r
101                         writer.Write ((byte) BinaryTypeCode.String);\r
102                         writer.Write (call.TypeName);\r
103 \r
104                         // Arguments\r
105 \r
106                         if ((methodFlags & MethodFlags.PrimitiveArguments) > 0)\r
107                         {\r
108                                 writer.Write ((uint)call.Args.Length);\r
109                                 for (int n=0; n<call.ArgCount; n++)\r
110                                 {\r
111                                         object arg = call.GetArg(n);\r
112                                         if (arg != null) {\r
113                                                 writer.Write (BinaryCommon.GetTypeCode (arg.GetType()));\r
114                                                 ObjectWriter.WritePrimitiveValue (writer, arg);\r
115                                         }\r
116                                         else\r
117                                                 writer.Write ((byte)BinaryTypeCode.Null);\r
118                                 }\r
119                         }\r
120 \r
121                         if ( infoArraySize > 0)\r
122                         {\r
123                                 object[] ainfo = new object[infoArraySize];\r
124                                 int n=0;\r
125                                 if ((methodFlags & MethodFlags.ArgumentsInMultiArray) > 0) ainfo[n++] = call.Args;\r
126                                 if ((methodFlags & MethodFlags.IncludesSignature) > 0) ainfo[n++] = call.MethodSignature;\r
127                                 if ((methodFlags & MethodFlags.IncludesLogicalCallContext) > 0) ainfo[n++] = call.LogicalCallContext;\r
128                                 if (extraProperties != null) ainfo[n++] = extraProperties;\r
129                                 info = ainfo;\r
130                         }\r
131                         else if ((methodFlags & MethodFlags.ArgumentsInSimpleArray) > 0)\r
132                                 info = call.Args;\r
133 \r
134                         if (info != null)\r
135                         {\r
136                                 ObjectWriter objectWriter = new ObjectWriter (surrogateSelector, context, assemblyFormat, typeFormat);\r
137                                 objectWriter.WriteObjectGraph (writer, info, headers);\r
138                         }\r
139                         else\r
140                                 writer.Write ((byte) BinaryElement.End);\r
141                 }\r
142 \r
143                 public static void WriteMethodResponse (BinaryWriter writer, object obj, Header[] headers, ISurrogateSelector surrogateSelector, StreamingContext context, FormatterAssemblyStyle assemblyFormat, FormatterTypeStyle typeFormat)\r
144                 {\r
145                         IMethodReturnMessage resp = (IMethodReturnMessage)obj;\r
146                         writer.Write ((byte) BinaryElement.MethodResponse);\r
147 \r
148                         string[] internalProperties = MethodReturnDictionary.InternalReturnKeys;\r
149 \r
150                         int infoArrayLength = 0;\r
151                         object info = null;\r
152                         object[] extraProperties = null;\r
153 \r
154                         // Type of return value\r
155 \r
156                         ReturnTypeTag returnTypeTag;\r
157 \r
158                         if (resp.Exception != null) {\r
159                                 returnTypeTag = ReturnTypeTag.Exception | ReturnTypeTag.Null;\r
160                                 info = new object[] {resp.Exception};\r
161                                 internalProperties = MethodReturnDictionary.InternalExceptionKeys;\r
162                         }\r
163                         else if (resp.ReturnValue == null) {\r
164                                 returnTypeTag = ReturnTypeTag.Null;\r
165                         }\r
166                         else if (IsMethodPrimitive(resp.ReturnValue.GetType())) {\r
167                                 returnTypeTag = ReturnTypeTag.PrimitiveType;\r
168                         }\r
169                         else {\r
170                                 returnTypeTag = ReturnTypeTag.ObjectType;\r
171                                 infoArrayLength++;\r
172                         }\r
173 \r
174                         // Message flags\r
175 \r
176                         MethodFlags contextFlag;\r
177                         MethodFlags formatFlag;\r
178 \r
179                         if ((resp.LogicalCallContext != null) && resp.LogicalCallContext.HasInfo && ((returnTypeTag & ReturnTypeTag.Exception) == 0)) \r
180                         {\r
181                                 contextFlag = MethodFlags.IncludesLogicalCallContext;\r
182                                 infoArrayLength++;\r
183                         }\r
184                         else\r
185                                 contextFlag = MethodFlags.ExcludeLogicalCallContext;\r
186 \r
187                         if (resp.Properties.Count > internalProperties.Length && ((returnTypeTag & ReturnTypeTag.Exception) == 0))\r
188                         {\r
189                                 extraProperties = GetExtraProperties (resp.Properties, internalProperties);\r
190                                 infoArrayLength++;\r
191                         }\r
192 \r
193                         if (resp.OutArgCount == 0)\r
194                                 formatFlag = MethodFlags.NoArguments;\r
195                         else \r
196                         {\r
197                                 if (AllTypesArePrimitive (resp.Args)) \r
198                                         formatFlag = MethodFlags.PrimitiveArguments;\r
199                                 else \r
200                                 {\r
201                                         if (infoArrayLength == 0)\r
202                                                 formatFlag = MethodFlags.ArgumentsInSimpleArray; \r
203                                         else {\r
204                                                 formatFlag = MethodFlags.ArgumentsInMultiArray;\r
205                                                 infoArrayLength++;\r
206                                         }\r
207                                 }\r
208                         }\r
209 \r
210                         writer.Write ((byte) (contextFlag | formatFlag));\r
211                         writer.Write ((byte) returnTypeTag);\r
212 \r
213                         // FIXME: what are the following 2 bytes for?\r
214                         writer.Write ((byte) 0);\r
215                         writer.Write ((byte) 0);\r
216 \r
217                         // Arguments\r
218 \r
219                         if (returnTypeTag == ReturnTypeTag.PrimitiveType)\r
220                         {\r
221                                 writer.Write (BinaryCommon.GetTypeCode (resp.ReturnValue.GetType()));\r
222                                 ObjectWriter.WritePrimitiveValue (writer, resp.ReturnValue);\r
223                         }\r
224 \r
225                         if (formatFlag == MethodFlags.PrimitiveArguments)\r
226                         {\r
227                                 writer.Write ((uint)resp.ArgCount);\r
228                                 for (int n=0; n<resp.ArgCount; n++)\r
229                                 {\r
230                                         object val = resp.GetArg(n);\r
231                                         if (val != null) {\r
232                                                 writer.Write (BinaryCommon.GetTypeCode (val.GetType()));\r
233                                                 ObjectWriter.WritePrimitiveValue (writer, val);\r
234                                         }\r
235                                         else\r
236                                                 writer.Write ((byte)BinaryTypeCode.Null);\r
237                                 }\r
238                         }\r
239 \r
240                         if (infoArrayLength > 0)\r
241                         {\r
242                                 object[] infoArray = new object[infoArrayLength];\r
243                                 int n = 0;\r
244 \r
245                                 if (formatFlag == MethodFlags.ArgumentsInMultiArray)\r
246                                         infoArray[n++] = resp.Args;\r
247 \r
248                                 if (returnTypeTag == ReturnTypeTag.ObjectType)\r
249                                         infoArray[n++] = resp.ReturnValue;\r
250 \r
251                                 if (contextFlag == MethodFlags.IncludesLogicalCallContext)\r
252                                         infoArray[n++] = resp.LogicalCallContext;\r
253 \r
254                                 if (extraProperties != null)\r
255                                         infoArray[n++] = extraProperties;\r
256 \r
257                                 info = infoArray;\r
258                         }\r
259                         else if ((formatFlag & MethodFlags.ArgumentsInSimpleArray) > 0)\r
260                                 info = resp.Args;\r
261 \r
262                         if (info != null)\r
263                         {\r
264                                 ObjectWriter objectWriter = new ObjectWriter (surrogateSelector, context, assemblyFormat, typeFormat);\r
265                                 objectWriter.WriteObjectGraph (writer, info, headers);\r
266                         }\r
267                         else\r
268                                 writer.Write ((byte) BinaryElement.End);\r
269                 }\r
270 \r
271                 public static object ReadMethodCall (BinaryReader reader, bool hasHeaders, HeaderHandler headerHandler, BinaryFormatter formatter)\r
272                 {\r
273                         BinaryElement elem = (BinaryElement)reader.ReadByte();  // The element code\r
274                         if (elem != BinaryElement.MethodCall) throw new SerializationException("Invalid format. Expected BinaryElement.MethodCall, found " +  elem);\r
275 \r
276                         MethodFlags flags = (MethodFlags) reader.ReadByte();\r
277 \r
278                         // FIXME: find a meaning for those 3 bytes\r
279                         reader.ReadByte();\r
280                         reader.ReadByte();\r
281                         reader.ReadByte();\r
282 \r
283                         if (((BinaryTypeCode)reader.ReadByte()) != BinaryTypeCode.String) throw new SerializationException ("Invalid format");\r
284                         string methodName = reader.ReadString();\r
285 \r
286                         if (((BinaryTypeCode)reader.ReadByte()) != BinaryTypeCode.String) throw new SerializationException ("Invalid format");\r
287                         string className = reader.ReadString();\r
288 \r
289                         //bool hasContextInfo = (flags & MethodFlags.IncludesLogicalCallContext) > 0;\r
290 \r
291                         object[] arguments = null;\r
292                         object methodSignature = null;\r
293                         object callContext = null;\r
294                         object[] extraProperties = null;\r
295                         Header[] headers = null;\r
296 \r
297                         if ((flags & MethodFlags.PrimitiveArguments) > 0)\r
298                         {\r
299                                 uint count = reader.ReadUInt32();\r
300                                 arguments = new object[count];\r
301                                 for (int n=0; n<count; n++)\r
302                                 {\r
303                                         Type type = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
304                                         arguments[n] = ObjectReader.ReadPrimitiveTypeValue (reader, type);\r
305                                 }\r
306                         }\r
307 \r
308                         if ((flags & MethodFlags.NeedsInfoArrayMask) > 0)\r
309                         {\r
310                                 ObjectReader objectReader = new ObjectReader (formatter);\r
311 \r
312                                 object result;\r
313                                 objectReader.ReadObjectGraph (reader, hasHeaders, out result, out headers);\r
314                                 object[] msgInfo = (object[]) result;\r
315 \r
316                                 if ((flags & MethodFlags.ArgumentsInSimpleArray) > 0) {\r
317                                         arguments = msgInfo;\r
318                                 }\r
319                                 else\r
320                                 {\r
321                                         int n = 0;\r
322                                         if ((flags & MethodFlags.ArgumentsInMultiArray) > 0) {\r
323                                                 if (msgInfo.Length > 1) arguments = (object[]) msgInfo[n++];\r
324                                                 else arguments = new object[0];\r
325                                         }\r
326 \r
327                                         if ((flags & MethodFlags.IncludesSignature) > 0)\r
328                                                 methodSignature = msgInfo[n++];\r
329 \r
330                                         if ((flags & MethodFlags.IncludesLogicalCallContext) > 0) \r
331                                                 callContext = msgInfo[n++];\r
332 \r
333                                         if (n < msgInfo.Length)\r
334                                                 extraProperties = (object[]) msgInfo[n];\r
335                                 }\r
336                         }\r
337                         else {\r
338                                 reader.ReadByte ();     // Reads the stream ender\r
339                         }\r
340 \r
341                         if (arguments == null) arguments = new object[0];\r
342 \r
343                         string uri = null;\r
344                         if (headerHandler != null)\r
345                                 uri = headerHandler(headers) as string;\r
346 \r
347                         Header[] methodInfo = new Header[6];\r
348                         methodInfo[0] = new Header("__MethodName", methodName);\r
349                         methodInfo[1] = new Header("__MethodSignature", methodSignature);\r
350                         methodInfo[2] = new Header("__TypeName", className);\r
351                         methodInfo[3] = new Header("__Args", arguments);\r
352                         methodInfo[4] = new Header("__CallContext", callContext);\r
353                         methodInfo[5] = new Header("__Uri", uri);\r
354 \r
355                         MethodCall call = new MethodCall (methodInfo);\r
356 \r
357                         if (extraProperties != null) {\r
358                                 foreach (DictionaryEntry entry in extraProperties)\r
359                                         call.Properties [(string)entry.Key] = entry.Value;\r
360                         }\r
361 \r
362                         return call;\r
363                 }\r
364 \r
365                 public static object ReadMethodResponse (BinaryReader reader, bool hasHeaders, HeaderHandler headerHandler, IMethodCallMessage methodCallMessage, BinaryFormatter formatter)\r
366                 {\r
367                         BinaryElement elem = (BinaryElement)reader.ReadByte();  // The element code\r
368                         if (elem != BinaryElement.MethodResponse) throw new SerializationException("Invalid format. Expected BinaryElement.MethodResponse, found " +  elem);\r
369 \r
370                         MethodFlags flags = (MethodFlags) reader.ReadByte ();\r
371                         ReturnTypeTag typeTag = (ReturnTypeTag) reader.ReadByte ();\r
372                         bool hasContextInfo = (flags & MethodFlags.IncludesLogicalCallContext) > 0;\r
373 \r
374                         // FIXME: find a meaning for those 2 bytes\r
375                         reader.ReadByte();\r
376                         reader.ReadByte();\r
377 \r
378                         object returnValue = null;\r
379                         object[] outArgs = null;\r
380                         LogicalCallContext callContext = null;\r
381                         Exception exception = null;\r
382                         object[] extraProperties = null;\r
383                         Header[] headers = null;\r
384 \r
385                         if ((typeTag & ReturnTypeTag.PrimitiveType) > 0)\r
386                         {\r
387                                 Type type = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
388                                 returnValue = ObjectReader.ReadPrimitiveTypeValue (reader, type);\r
389                         }\r
390 \r
391                         if ((flags & MethodFlags.PrimitiveArguments) > 0)\r
392                         {\r
393                                 uint count = reader.ReadUInt32();\r
394                                 outArgs = new object[count];\r
395                                 for (int n=0; n<count; n++) {\r
396                                         Type type = BinaryCommon.GetTypeFromCode (reader.ReadByte());\r
397                                         outArgs[n] = ObjectReader.ReadPrimitiveTypeValue (reader, type);\r
398                                 }\r
399                         }\r
400 \r
401                         if (hasContextInfo || (typeTag & ReturnTypeTag.ObjectType) > 0 || \r
402                                 (typeTag & ReturnTypeTag.Exception) > 0 ||\r
403                                 (flags & MethodFlags.ArgumentsInSimpleArray) > 0 || \r
404                                 (flags & MethodFlags.ArgumentsInMultiArray) > 0)\r
405                         {\r
406                                 // There objects that need to be deserialized using an ObjectReader\r
407 \r
408                                 ObjectReader objectReader = new ObjectReader (formatter);\r
409                                 object result;\r
410                                 objectReader.ReadObjectGraph (reader, hasHeaders, out result, out headers);\r
411                                 object[] msgInfo = (object[]) result;\r
412 \r
413                                 if ((typeTag & ReturnTypeTag.Exception) > 0) {\r
414                                         exception = (Exception) msgInfo[0];\r
415                                 }\r
416                                 else if ((flags & MethodFlags.NoArguments) > 0 || (flags & MethodFlags.PrimitiveArguments) > 0) {\r
417                                         int n = 0;\r
418                                         if ((typeTag & ReturnTypeTag.ObjectType) > 0) returnValue = msgInfo [n++];\r
419                                         if (hasContextInfo) callContext = (LogicalCallContext)msgInfo[n++];\r
420                                         if (n < msgInfo.Length) extraProperties = (object[]) msgInfo[n];\r
421                                 }\r
422                                 else if ((flags & MethodFlags.ArgumentsInSimpleArray) > 0) {\r
423                                         outArgs = msgInfo;\r
424                                 }\r
425                                 else {\r
426                                         int n = 0;\r
427                                         outArgs = (object[]) msgInfo[n++];\r
428                                         if ((typeTag & ReturnTypeTag.ObjectType) > 0) returnValue = msgInfo[n++];\r
429                                         if (hasContextInfo) callContext = (LogicalCallContext)msgInfo[n++];\r
430                                         if (n < msgInfo.Length) extraProperties = (object[]) msgInfo[n];\r
431                                 }\r
432                         }\r
433                         else {\r
434                                 reader.ReadByte ();     // Reads the stream ender\r
435                         }\r
436 \r
437                         if (headerHandler != null) \r
438                                 headerHandler(headers);\r
439 \r
440                         if (exception != null)\r
441                                 return new ReturnMessage (exception, methodCallMessage);\r
442                         else\r
443                         {\r
444                                 int argCount = (outArgs!=null) ? outArgs.Length : 0;\r
445                                 ReturnMessage result = new ReturnMessage (returnValue, outArgs, argCount, callContext, methodCallMessage);\r
446 \r
447                                 if (extraProperties != null) {\r
448                                         foreach (DictionaryEntry entry in extraProperties)\r
449                                                 result.Properties [(string)entry.Key] = entry.Value;\r
450                                 }\r
451 \r
452                                 return result;\r
453                         }\r
454                 }\r
455 \r
456                 private static bool AllTypesArePrimitive(object[] objects)\r
457                 {\r
458                         foreach (object ob in objects) \r
459                         {\r
460                                 if (ob != null && !IsMethodPrimitive(ob.GetType())) \r
461                                         return false;\r
462                         }\r
463                         return true;\r
464                 }\r
465 \r
466                 // When serializing methods, string are considered primitive types\r
467                 public static bool IsMethodPrimitive (Type type)\r
468                 {\r
469                         return type.IsPrimitive || type == typeof(string) || type == typeof (DateTime) || type == typeof (Decimal);\r
470                 }\r
471 \r
472                 static object[] GetExtraProperties (IDictionary properties, string[] internalKeys)\r
473                 {\r
474                         object[] extraProperties = new object [properties.Count - internalKeys.Length];\r
475                         \r
476                         int n = 0;\r
477                         IDictionaryEnumerator e = properties.GetEnumerator();\r
478                         while (e.MoveNext())\r
479                                 if (!IsInternalKey ((string) e.Entry.Key, internalKeys)) extraProperties [n++] = e.Entry;\r
480 \r
481                         return extraProperties;\r
482                 }\r
483 \r
484                 static bool IsInternalKey (string key, string[] internalKeys)\r
485                 {\r
486                         foreach (string ikey in internalKeys)\r
487                                 if (key == ikey) return true;\r
488                         return false;\r
489                 }\r
490 \r
491         }\r
492 }\r