[Remoting] Moved helper methods from CAD call class to parent class to enable remotin...
[mono.git] / mcs / class / corlib / System.Runtime.Remoting.Messaging / CADMessages.cs
1 //
2 // System.Runtime.Remoting.Messaging.CADMessages.cs
3 //
4 // Author:
5 //   Patrik Torstensson
6 //
7 // (C) Patrik Torstensson
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.IO;
36 using System.Reflection;
37 using System.Runtime.Serialization;
38 using System.Runtime.Remoting;
39 using System.Runtime.Remoting.Channels;
40 using System.Runtime.Remoting.Messaging;
41 using System.Runtime.Remoting.Proxies;
42
43 namespace System.Runtime.Remoting.Messaging {
44
45         internal class CADArgHolder {
46                 public int index;
47
48                 public CADArgHolder (int i) {
49                         index = i;
50                 }
51         }
52         
53         internal class CADObjRef {
54                 ObjRef objref;
55                 public int SourceDomain;
56
57                 public CADObjRef (ObjRef o, int sourceDomain) {
58                         objref = o;
59                         SourceDomain = sourceDomain;
60                 }
61                 
62                 public string TypeName {
63                         get { return objref.TypeInfo.TypeName; }
64                 }
65                 
66                 public string URI {
67                         get { return objref.URI; }
68                 }
69         }
70
71         internal class CADMessageBase {
72
73                 protected object [] _args;
74                 protected byte [] _serializedArgs = null;
75                 protected int _propertyCount = 0;
76                 protected CADArgHolder _callContext;
77                 internal RuntimeMethodHandle MethodHandle;
78                 internal string FullTypeName;
79                 internal MethodBase _method;
80
81                 public CADMessageBase (IMethodMessage msg) {
82                         MethodHandle = msg.MethodBase.MethodHandle;
83                         FullTypeName = msg.MethodBase.DeclaringType.AssemblyQualifiedName;
84                 }
85
86                 internal MethodBase method {
87                         get {
88                                 if (_method == null) {
89                                         _method = GetMethod();
90                                 }
91                                 return _method;
92                         }
93                 }
94
95                 internal MethodBase GetMethod ()
96                 {
97                         Type tt = Type.GetType (FullTypeName, true);
98                         if (tt.IsGenericType || tt.IsGenericTypeDefinition) {
99                                 _method = MethodBase.GetMethodFromHandleNoGenericCheck (MethodHandle);
100                         } else {
101                                 _method = MethodBase.GetMethodFromHandle (MethodHandle);
102                         }
103
104                         if (tt != _method.DeclaringType) {
105                                 // The target domain has loaded the type from a different assembly.
106                                 // We need to locate the correct type and get the method from it
107                                 Type [] signature = GetSignature (_method, true);
108                                 if (_method.IsGenericMethod) {
109                                         MethodBase [] methods = tt.GetMethods (BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
110                                         Type [] base_args = _method.GetGenericArguments ();
111                                         foreach (MethodBase method in methods) {
112                                                 if (!method.IsGenericMethod || method.Name != _method.Name)
113                                                         continue;
114                                                 Type [] method_args = method.GetGenericArguments ();
115                                                 if (base_args.Length != method_args.Length)
116                                                         continue;
117
118                                                 MethodInfo method_instance = ((MethodInfo) method).MakeGenericMethod (base_args);
119                                                 Type [] base_sig = GetSignature (method_instance, false);
120                                                 if (base_sig.Length != signature.Length) {
121                                                         continue;
122                                                 }
123                                                 bool dont = false;
124                                                 for (int i = base_sig.Length - 1; i >= 0; i--) {
125                                                         if (base_sig [i] != signature [i]) {
126                                                                 dont = true;
127                                                                 break;
128                                                         }
129                                                 }
130                                                 if (dont)
131                                                         continue;
132                                                 return method_instance;
133                                         }
134                                         return _method;
135                                 }
136
137                                 MethodBase mb = tt.GetMethod (_method.Name, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance, null, signature, null);
138                                 if (mb == null)
139                                         throw new RemotingException ("Method '" + _method.Name + "' not found in type '" + tt + "'");
140                                 return mb;
141                         }
142                         return _method;
143                 }
144
145                 static protected Type [] GetSignature (MethodBase methodBase, bool load)
146                 {
147                         ParameterInfo[] pars = methodBase.GetParameters ();
148                         Type[] signature = new Type [pars.Length];
149                         for (int n=0; n<pars.Length; n++) {
150                                 // The parameter types may also be loaded from a different assembly, so we need
151                                 // to load them again
152                                 if (load)
153                                         signature [n] = Type.GetType (pars [n].ParameterType.AssemblyQualifiedName, true);
154                                 else
155                                         signature [n] = pars [n].ParameterType;
156                         }
157                         return signature;
158                 }
159                 // Helper to marshal properties
160                 internal static int MarshalProperties (IDictionary dict, ref ArrayList args) {
161                         IDictionary serDict = dict;
162                         int count = 0;
163
164                         MethodDictionary msgDict = dict as MethodDictionary;
165                         if (null != msgDict) {
166                                 if (msgDict.HasInternalProperties) {
167                                         serDict = msgDict.InternalProperties;
168                                         if (null != serDict) {
169                                                 foreach (DictionaryEntry e in serDict) {
170                                                         if (null == args)
171                                                                 args = new ArrayList();
172                                                         args.Add(e);
173                                                         count++;
174                                                 }
175                                         }
176                                 }
177                         } else {
178                                 if (null != dict) {
179                                         foreach (DictionaryEntry e in serDict) {
180                                                 if (null == args)
181                                                         args = new ArrayList();
182                                                 args.Add(e);
183                                                 count++;
184                                         }
185                                 }
186                         }
187
188                         return count;
189                 }
190
191                 internal static void UnmarshalProperties (IDictionary dict, int count, ArrayList args) {
192                         for (int i = 0; i < count; i++) {
193                                 DictionaryEntry e = (DictionaryEntry) args [i];
194                                 dict [e.Key] = e.Value;
195                         }
196                 }
197
198                 // We can ignore marshalling for string and primitive types
199                 private static bool IsPossibleToIgnoreMarshal (object obj) {
200
201                         Type objType = obj.GetType();
202                         if (objType.IsPrimitive || objType == typeof(void))
203                                 return true;
204                         
205                         if (objType.IsArray && objType.GetElementType().IsPrimitive && ((Array)obj).Rank == 1)
206                                 return true;
207                                 
208                         if (obj is string || obj is DateTime || obj is TimeSpan)
209                                 return true;
210                                 
211                         return false;
212                 }
213
214                 // Checks an argument if it's possible to pass without marshalling and
215                 // if not it will be added to arguments to be serialized
216                 protected object MarshalArgument (object arg, ref ArrayList args) {
217                         if (null == arg)
218                                 return null;
219
220                         if (IsPossibleToIgnoreMarshal (arg))
221                                 return arg;
222
223                         MarshalByRefObject mbr = arg as MarshalByRefObject;
224                         if (null != mbr)
225                         {
226                                 if (RemotingServices.IsTransparentProxy(mbr)) {
227                                         // We don't deal with this case yet
228                                 }
229                                 else {
230                                         ObjRef objRef = RemotingServices.Marshal(mbr);
231                                         return new CADObjRef(objRef, System.Threading.Thread.GetDomainID());
232                                 }
233                         }
234
235                         if (null == args)
236                                 args = new ArrayList();
237                         
238                         args.Add (arg);
239                         
240                         // return position that the arg exists in the serialized list
241                         return new CADArgHolder(args.Count - 1);
242                 }
243
244                 protected object UnmarshalArgument (object arg, ArrayList args) {
245                         if (arg == null) return null;
246                         
247                         // Check if argument is an holder (then we know that it's a serialized argument)
248                         CADArgHolder holder = arg as CADArgHolder;
249                         if (null != holder) {
250                                 return args [holder.index];
251                         }
252
253                         CADObjRef objref = arg as CADObjRef;
254                         if (null != objref) {
255                                 string typeName = string.Copy (objref.TypeName);
256                                 string uri = string.Copy (objref.URI);
257                                 int domid = objref.SourceDomain;
258                                 
259                                 ChannelInfo cinfo = new ChannelInfo (new CrossAppDomainData (domid));
260                                 ObjRef localRef = new ObjRef (typeName, uri, cinfo);
261                                 return RemotingServices.Unmarshal (localRef);
262                         }
263                         
264                         if (arg is Array)
265                         {
266                                 Array argb = (Array)arg;
267                                 Array argn;
268                                 
269                                 // We can't use Array.CreateInstance (arg.GetType().GetElementType()) because
270                                 // GetElementType() returns a type from the source domain.
271                                 
272                                 switch (Type.GetTypeCode (arg.GetType().GetElementType()))
273                                 {
274                                         case TypeCode.Boolean: argn = new bool [argb.Length]; break;
275                                         case TypeCode.Byte: argn = new Byte [argb.Length]; break;
276                                         case TypeCode.Char: argn = new Char [argb.Length]; break;
277                                         case TypeCode.Decimal: argn = new Decimal [argb.Length]; break;
278                                         case TypeCode.Double: argn = new Double [argb.Length]; break;
279                                         case TypeCode.Int16: argn = new Int16 [argb.Length]; break;
280                                         case TypeCode.Int32: argn = new Int32 [argb.Length]; break;
281                                         case TypeCode.Int64: argn = new Int64 [argb.Length]; break;
282                                         case TypeCode.SByte: argn = new SByte [argb.Length]; break;
283                                         case TypeCode.Single: argn = new Single [argb.Length]; break;
284                                         case TypeCode.UInt16: argn = new UInt16 [argb.Length]; break;
285                                         case TypeCode.UInt32: argn = new UInt32 [argb.Length]; break;
286                                         case TypeCode.UInt64: argn = new UInt64 [argb.Length]; break;
287                                         default: throw new NotSupportedException ();
288                                 }
289                                 
290                                 argb.CopyTo (argn, 0);
291                                 return argn;
292                         }
293
294                         switch (Type.GetTypeCode (arg.GetType()))
295                         {
296                                 case TypeCode.Boolean: return (bool)arg;
297                                 case TypeCode.Byte: return (byte)arg;
298                                 case TypeCode.Char: return (char)arg;
299                                 case TypeCode.Decimal: return (decimal)arg;
300                                 case TypeCode.Double: return (double)arg;
301                                 case TypeCode.Int16: return (Int16)arg;
302                                 case TypeCode.Int32: return (Int32)arg;
303                                 case TypeCode.Int64: return (Int64)arg;
304                                 case TypeCode.SByte: return (SByte)arg;
305                                 case TypeCode.Single: return (Single)arg;
306                                 case TypeCode.UInt16: return (UInt16)arg;
307                                 case TypeCode.UInt32: return (UInt32)arg;
308                                 case TypeCode.UInt64: return (UInt64)arg;
309                                 case TypeCode.String: return string.Copy ((string) arg);
310                                 case TypeCode.DateTime: return new DateTime (((DateTime)arg).Ticks);
311                                 default:
312                                         if (arg is TimeSpan) return new TimeSpan (((TimeSpan)arg).Ticks);
313                                         if (arg is IntPtr) return (IntPtr) arg;
314                                         break;
315                         }       
316
317                         throw new NotSupportedException ("Parameter of type " + arg.GetType () + " cannot be unmarshalled");
318                 }
319
320                 internal object [] MarshalArguments (object [] arguments, ref ArrayList args) {
321                         object [] marshalledArgs = new object [arguments.Length];
322
323                         int total = arguments.Length;
324                         for (int i = 0; i < total; i++)
325                                 marshalledArgs [i] = MarshalArgument (arguments [i], ref args);
326
327                         return marshalledArgs;
328                 }
329
330                 internal object [] UnmarshalArguments (object [] arguments, ArrayList args) {
331                         object [] unmarshalledArgs = new object [arguments.Length];
332
333                         int total = arguments.Length;
334                         for (int i = 0; i < total; i++)
335                                 unmarshalledArgs [i] = UnmarshalArgument (arguments [i], args);
336
337                         return unmarshalledArgs;
338                 }
339                 
340                 protected void SaveLogicalCallContext (IMethodMessage msg, ref ArrayList serializeList)
341                 {
342                         if (msg.LogicalCallContext != null && msg.LogicalCallContext.HasInfo) 
343                         {
344                                 if (serializeList == null)
345                                         serializeList = new ArrayList();
346
347                                 _callContext = new CADArgHolder (serializeList.Count);
348                                 serializeList.Add (msg.LogicalCallContext);
349                         }
350                 }
351                 
352                 internal LogicalCallContext GetLogicalCallContext (ArrayList args) 
353                 {
354                         if (null == _callContext)
355                                 return null;
356
357                         return (LogicalCallContext) args [_callContext.index];
358                 }
359         }
360
361         // Used when passing a IMethodCallMessage between appdomains
362         internal class CADMethodCallMessage : CADMessageBase {
363                 string _uri;
364
365                 internal string Uri {
366                         get {
367                                 return _uri;
368                         }
369                 }
370
371                 static internal CADMethodCallMessage Create (IMessage callMsg) {
372                         IMethodCallMessage msg = callMsg as IMethodCallMessage;
373                         if (null == msg)
374                                 return null;
375
376                         return new CADMethodCallMessage (msg);
377                 }
378
379                 internal CADMethodCallMessage (IMethodCallMessage callMsg): base (callMsg) {
380                         _uri = callMsg.Uri;
381
382                         ArrayList serializeList = null; 
383                         
384                         _propertyCount = MarshalProperties (callMsg.Properties, ref serializeList);
385
386                         _args = MarshalArguments ( callMsg.Args, ref serializeList);
387
388                         // Save callcontext
389                         SaveLogicalCallContext (callMsg, ref serializeList);
390                         
391                         // Serialize message data if needed
392
393                         if (null != serializeList) {
394                                 MemoryStream stm = CADSerializer.SerializeObject (serializeList.ToArray());
395                                 _serializedArgs = stm.GetBuffer();
396                         }
397                 }
398
399                 internal ArrayList GetArguments () {
400                         ArrayList ret = null;
401
402                         if (null != _serializedArgs) {
403                                 object[] oret = (object[]) CADSerializer.DeserializeObject (new MemoryStream (_serializedArgs));
404                                 ret = new ArrayList (oret);
405                                 _serializedArgs = null;
406                         }
407
408                         return ret;
409                 }
410
411                 internal object [] GetArgs (ArrayList args) {
412                         return UnmarshalArguments (_args, args);
413                 }
414
415                 internal int PropertiesCount {
416                         get {
417                                 return _propertyCount;
418                         }
419                 }
420                 
421         }
422         
423         // Used when passing a IMethodReturnMessage between appdomains
424         internal class CADMethodReturnMessage : CADMessageBase {
425                 object _returnValue;
426                 CADArgHolder _exception = null;
427
428                 static internal CADMethodReturnMessage Create (IMessage callMsg) {
429                         IMethodReturnMessage msg = callMsg as IMethodReturnMessage;
430                         if (null == msg)
431                                 return null;
432
433                         return new CADMethodReturnMessage (msg);
434                 }
435
436                 internal CADMethodReturnMessage(IMethodReturnMessage retMsg): base (retMsg) {
437                         ArrayList serializeList = null; 
438                         
439                         _propertyCount = MarshalProperties (retMsg.Properties, ref serializeList);
440
441                         _returnValue = MarshalArgument ( retMsg.ReturnValue, ref serializeList);
442                         _args = MarshalArguments ( retMsg.Args, ref serializeList);
443
444                         if (null != retMsg.Exception) {
445                                 if (null == serializeList)
446                                         serializeList = new ArrayList();
447                                 
448                                 _exception = new CADArgHolder (serializeList.Count);
449                                 serializeList.Add(retMsg.Exception);
450                         }
451
452                         // Save callcontext
453                         SaveLogicalCallContext (retMsg, ref serializeList);
454
455                         if (null != serializeList) {
456                                 MemoryStream stm = CADSerializer.SerializeObject (serializeList.ToArray());
457                                 _serializedArgs = stm.GetBuffer();
458                         }
459                 }
460
461                 internal ArrayList GetArguments () {
462                         ArrayList ret = null;
463
464                         if (null != _serializedArgs) {
465                                 object[] oret = (object[]) CADSerializer.DeserializeObject (new MemoryStream (_serializedArgs));
466                                 ret = new ArrayList (oret);
467                                 _serializedArgs = null;
468                         }
469
470                         return ret;
471                 }
472
473                 internal object [] GetArgs (ArrayList args) {
474                         return UnmarshalArguments (_args, args);
475                 }
476                         
477                 internal object GetReturnValue (ArrayList args) {
478                         return UnmarshalArgument (_returnValue, args);
479                 }
480
481                 internal Exception GetException(ArrayList args) {
482                         if (null == _exception)
483                                 return null;
484
485                         return (Exception) args [_exception.index]; 
486                 }
487
488                 internal int PropertiesCount {
489                         get {
490                                 return _propertyCount;
491                         }
492                 }
493         }
494 }