1 //------------------------------------------------------------------------------
2 // <copyright file="LogicalMethodInfo.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System
.Web
.Services
.Protocols
{
10 using System
.Web
.Services
;
11 using System
.Reflection
;
12 using System
.Collections
;
13 using System
.Security
.Permissions
;
14 using System
.Globalization
;
16 using System
.Security
.Cryptography
;
18 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes"]/*' />
20 /// <para>[To be supplied.]</para>
22 public enum LogicalMethodTypes
{
23 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes.Sync"]/*' />
25 /// <para>[To be supplied.]</para>
28 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes.Async"]/*' />
30 /// <para>[To be supplied.]</para>
36 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo"]/*' />
38 /// <para>[To be supplied.]</para>
40 public sealed class LogicalMethodInfo
{
41 MethodInfo methodInfo
;
42 MethodInfo endMethodInfo
;
43 ParameterInfo
[] inParams
;
44 ParameterInfo
[] outParams
;
45 ParameterInfo
[] parameters
;
48 ParameterInfo callbackParam
;
49 ParameterInfo stateParam
;
50 ParameterInfo resultParam
;
53 static object[] emptyObjectArray
= new object[0];
54 WebServiceBindingAttribute binding
;
55 WebMethodAttribute attribute
;
56 MethodInfo declaration
;
57 static HashAlgorithm hash
;
59 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.LogicalMethodInfo"]/*' />
61 /// <para>[To be supplied.]</para>
63 public LogicalMethodInfo(MethodInfo methodInfo
) : this (methodInfo
, null) {
66 internal LogicalMethodInfo(MethodInfo methodInfo
, WebMethod webMethod
) {
67 if (methodInfo
.IsStatic
) throw new InvalidOperationException(Res
.GetString(Res
.WebMethodStatic
, methodInfo
.Name
));
68 this.methodInfo
= methodInfo
;
69 if (webMethod
!= null) {
70 this.binding
= webMethod
.binding
;
71 this.attribute
= webMethod
.attribute
;
72 this.declaration
= webMethod
.declaration
;
75 MethodInfo methodDefinition
= declaration
!= null ? declaration
: methodInfo
;
76 parameters
= methodDefinition
.GetParameters();
77 inParams
= GetInParameters(methodDefinition
, parameters
, 0, parameters
.Length
, false);
78 outParams
= GetOutParameters(methodDefinition
, parameters
, 0, parameters
.Length
, false);
79 retType
= methodDefinition
.ReturnType
;
80 isVoid
= retType
== typeof(void);
81 methodName
= methodDefinition
.Name
;
82 attributes
= new Hashtable();
85 LogicalMethodInfo(MethodInfo beginMethodInfo
, MethodInfo endMethodInfo
, WebMethod webMethod
) {
86 this.methodInfo
= beginMethodInfo
;
87 this.endMethodInfo
= endMethodInfo
;
88 methodName
= beginMethodInfo
.Name
.Substring(5);
89 if (webMethod
!= null) {
90 this.binding
= webMethod
.binding
;
91 this.attribute
= webMethod
.attribute
;
92 this.declaration
= webMethod
.declaration
;
94 ParameterInfo
[] beginParamInfos
= beginMethodInfo
.GetParameters();
95 if (beginParamInfos
.Length
< 2 ||
96 beginParamInfos
[beginParamInfos
.Length
- 1].ParameterType
!= typeof(object) ||
97 beginParamInfos
[beginParamInfos
.Length
- 2].ParameterType
!= typeof(AsyncCallback
)) {
98 throw new InvalidOperationException(Res
.GetString(Res
.WebMethodMissingParams
, beginMethodInfo
.DeclaringType
.FullName
, beginMethodInfo
.Name
,
99 typeof(AsyncCallback
).FullName
, typeof(object).FullName
));
102 stateParam
= beginParamInfos
[beginParamInfos
.Length
- 1];
103 callbackParam
= beginParamInfos
[beginParamInfos
.Length
- 2];
105 inParams
= GetInParameters(beginMethodInfo
, beginParamInfos
, 0, beginParamInfos
.Length
- 2, true);
107 ParameterInfo
[] endParamInfos
= endMethodInfo
.GetParameters();
108 resultParam
= endParamInfos
[0];
109 outParams
= GetOutParameters(endMethodInfo
, endParamInfos
, 1, endParamInfos
.Length
- 1, true);
111 parameters
= new ParameterInfo
[inParams
.Length
+ outParams
.Length
];
112 inParams
.CopyTo(parameters
, 0);
113 outParams
.CopyTo(parameters
, inParams
.Length
);
115 retType
= endMethodInfo
.ReturnType
;
116 isVoid
= retType
== typeof(void);
117 attributes
= new Hashtable();
120 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.ToString"]/*' />
122 /// <para>[To be supplied.]</para>
124 public override string ToString() {
125 return methodInfo
.ToString();
128 // This takes in parameters, and returns return value followed by out parameters in an array
129 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Invoke"]/*' />
131 /// <para>[To be supplied.]</para>
133 [PermissionSet(SecurityAction
.LinkDemand
, Name
= "FullTrust")]
134 public object[] Invoke(object target
, object[] values
) {
135 if (outParams
.Length
> 0) {
136 object[] newValues
= new object[parameters
.Length
];
137 for (int i
= 0; i
< inParams
.Length
; i
++) {
138 newValues
[inParams
[i
].Position
] = values
[i
];
142 object returnValue
= methodInfo
.Invoke(target
, values
);
143 if (outParams
.Length
> 0) {
144 int count
= outParams
.Length
;
145 if (!isVoid
) count
++;
146 object[] results
= new object[count
];
148 if (!isVoid
) results
[count
++] = returnValue
;
149 for (int i
= 0; i
< outParams
.Length
; i
++) {
150 results
[count
++] = values
[outParams
[i
].Position
];
155 return emptyObjectArray
;
158 return new object[] { returnValue }
;
162 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.BeginInvoke"]/*' />
164 /// <para>[To be supplied.]</para>
166 [PermissionSet(SecurityAction
.LinkDemand
, Name
= "FullTrust")]
167 public IAsyncResult
BeginInvoke(object target
, object[] values
, AsyncCallback callback
, object asyncState
) {
168 object[] asyncValues
= new object[values
.Length
+ 2];
169 values
.CopyTo(asyncValues
, 0);
170 asyncValues
[values
.Length
] = callback
;
171 asyncValues
[values
.Length
+ 1] = asyncState
;
172 return (IAsyncResult
)methodInfo
.Invoke(target
, asyncValues
);
175 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.EndInvoke"]/*' />
177 /// <para>[To be supplied.]</para>
179 [PermissionSet(SecurityAction
.LinkDemand
, Name
= "FullTrust")]
180 public object[] EndInvoke(object target
, IAsyncResult asyncResult
) {
181 object[] values
= new object[outParams
.Length
+ 1];
182 values
[0] = asyncResult
;
183 object returnValue
= endMethodInfo
.Invoke(target
, values
);
185 values
[0] = returnValue
;
188 else if (outParams
.Length
> 0) {
189 object[] newValues
= new object[outParams
.Length
];
190 Array
.Copy(values
, 1, newValues
, 0, newValues
.Length
);
194 return emptyObjectArray
;
198 internal WebServiceBindingAttribute Binding
{
199 get { return binding; }
202 internal MethodInfo Declaration
{
203 get { return declaration; }
206 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.DeclaringType"]/*' />
208 /// <para>[To be supplied.]</para>
210 public Type DeclaringType
{
211 get { return methodInfo.DeclaringType; }
214 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Name"]/*' />
216 /// <para>[To be supplied.]</para>
219 get { return methodName; }
222 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncResultParameter"]/*' />
224 /// <para>[To be supplied.]</para>
226 public ParameterInfo AsyncResultParameter
{
227 get { return resultParam; }
230 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncCallbackParameter"]/*' />
232 /// <para>[To be supplied.]</para>
234 public ParameterInfo AsyncCallbackParameter
{
235 get { return callbackParam; }
238 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncStateParameter"]/*' />
240 /// <para>[To be supplied.]</para>
242 public ParameterInfo AsyncStateParameter
{
243 get { return stateParam; }
246 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.ReturnType"]/*' />
248 /// <para>[To be supplied.]</para>
250 public Type ReturnType
{
251 get { return retType; }
254 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsVoid"]/*' />
256 /// <para>[To be supplied.]</para>
259 get { return isVoid; }
262 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsAsync"]/*' />
264 /// <para>[To be supplied.]</para>
266 public bool IsAsync
{
267 get { return endMethodInfo != null; }
270 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.InParameters"]/*' />
272 /// <para>[To be supplied.]</para>
274 public ParameterInfo
[] InParameters
{
275 get { return inParams; }
278 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.OutParameters"]/*' />
280 /// <para>[To be supplied.]</para>
282 public ParameterInfo
[] OutParameters
{
283 get { return outParams; }
286 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Parameters"]/*' />
288 /// <para>[To be supplied.]</para>
290 public ParameterInfo
[] Parameters
{
291 get { return parameters; }
294 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.GetCustomAttributes"]/*' />
296 /// <para>[To be supplied.]</para>
298 public object[] GetCustomAttributes(Type type
) {
299 object[] attrForType
= null;
300 attrForType
= (object[])attributes
[type
];
301 if (attrForType
!= null)
304 attrForType
= (object[])attributes
[type
];
305 if (attrForType
== null) {
306 if (declaration
!= null) {
307 object[] declAttributes
= declaration
.GetCustomAttributes(type
, false);
308 object[] implAttributes
= methodInfo
.GetCustomAttributes(type
, false);
309 if (implAttributes
.Length
> 0) {
310 if (CanMerge(type
)) {
311 ArrayList all
= new ArrayList();
312 for (int i
= 0; i
< declAttributes
.Length
; i
++) {
313 all
.Add(declAttributes
[i
]);
315 for (int i
= 0; i
< implAttributes
.Length
; i
++) {
316 all
.Add(implAttributes
[i
]);
318 attrForType
= (object[])all
.ToArray(type
);
321 throw new InvalidOperationException(Res
.GetString(Res
.ContractOverride
, methodInfo
.Name
, methodInfo
.DeclaringType
.FullName
, declaration
.DeclaringType
.FullName
, declaration
.ToString(), implAttributes
[0].ToString()));
325 attrForType
= declAttributes
;
329 attrForType
= methodInfo
.GetCustomAttributes(type
, false);
331 attributes
[type
] = attrForType
;
338 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.GetCustomAttribute"]/*' />
340 /// <para>[To be supplied.]</para>
342 public object GetCustomAttribute(Type type
) {
343 object[] attrs
= GetCustomAttributes(type
);
344 if (attrs
.Length
== 0) return null;
348 internal WebMethodAttribute MethodAttribute
{
350 if (attribute
== null) {
351 attribute
= (WebMethodAttribute
)GetCustomAttribute(typeof(WebMethodAttribute
));
352 if (attribute
== null) {
353 attribute
= new WebMethodAttribute();
360 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.CustomAttributeProvider"]/*' />
362 /// <para>[To be supplied.]</para>
364 public ICustomAttributeProvider CustomAttributeProvider
{
365 // Custom attributes are always on the XXX (sync) or BeginXXX (async) method.
366 get { return methodInfo; }
369 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.ReturnTypeCustomAttributeProvider"]/*' />
371 /// <para>[To be supplied.]</para>
373 public ICustomAttributeProvider ReturnTypeCustomAttributeProvider
{
375 if (declaration
!= null)
376 return declaration
.ReturnTypeCustomAttributes
;
377 return methodInfo
.ReturnTypeCustomAttributes
;
381 // Do not use this to property get custom attributes. Instead use the CustomAttributeProvider
382 // property which automatically handles where the custom attributes belong for async methods
383 // (which are actually two methods: BeginXXX and EndXXX).
384 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.MethodInfo"]/*' />
386 /// <para>[To be supplied.]</para>
388 public MethodInfo MethodInfo
{
389 get { return endMethodInfo == null ? methodInfo : null; }
392 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.BeginMethodInfo"]/*' />
394 /// <para>[To be supplied.]</para>
396 public MethodInfo BeginMethodInfo
{
397 get { return methodInfo; }
400 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.EndMethodInfo"]/*' />
402 /// <para>[To be supplied.]</para>
404 public MethodInfo EndMethodInfo
{
405 get { return endMethodInfo; }
408 static ParameterInfo
[] GetInParameters(MethodInfo methodInfo
, ParameterInfo
[] paramInfos
, int start
, int length
, bool mustBeIn
) {
410 for (int i
= 0; i
< length
; i
++) {
411 ParameterInfo paramInfo
= paramInfos
[i
+ start
];
412 if (IsInParameter(paramInfo
)) {
416 throw new InvalidOperationException(Res
.GetString(Res
.WebBadOutParameter
, paramInfo
.Name
, methodInfo
.DeclaringType
.FullName
, paramInfo
.Name
));
420 ParameterInfo
[] ins
= new ParameterInfo
[count
];
422 for (int i
= 0; i
< length
; i
++) {
423 ParameterInfo paramInfo
= paramInfos
[i
+ start
];
424 if (IsInParameter(paramInfo
)) {
425 ins
[count
++] = paramInfo
;
431 static ParameterInfo
[] GetOutParameters(MethodInfo methodInfo
, ParameterInfo
[] paramInfos
, int start
, int length
, bool mustBeOut
) {
433 for (int i
= 0; i
< length
; i
++) {
434 ParameterInfo paramInfo
= paramInfos
[i
+ start
];
435 if (IsOutParameter(paramInfo
)) {
438 else if (mustBeOut
) {
439 throw new InvalidOperationException(Res
.GetString(Res
.WebInOutParameter
, paramInfo
.Name
, methodInfo
.DeclaringType
.FullName
, paramInfo
.Name
));
443 ParameterInfo
[] outs
= new ParameterInfo
[count
];
445 for (int i
= 0; i
< length
; i
++) {
446 ParameterInfo paramInfo
= paramInfos
[i
+ start
];
447 if (IsOutParameter(paramInfo
)) {
448 outs
[count
++] = paramInfo
;
454 static bool IsInParameter(ParameterInfo paramInfo
) {
455 return !paramInfo
.IsOut
;
458 static bool IsOutParameter(ParameterInfo paramInfo
) {
459 return paramInfo
.IsOut
|| paramInfo
.ParameterType
.IsByRef
;
462 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsBeginMethod"]/*' />
464 /// <para>[To be supplied.]</para>
466 public static bool IsBeginMethod(MethodInfo methodInfo
) {
467 return typeof(IAsyncResult
).IsAssignableFrom(methodInfo
.ReturnType
) &&
468 methodInfo
.Name
.StartsWith("Begin", StringComparison
.Ordinal
);
471 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsEndMethod"]/*' />
473 /// <para>[To be supplied.]</para>
475 public static bool IsEndMethod(MethodInfo methodInfo
) {
476 ParameterInfo
[] paramInfos
= methodInfo
.GetParameters();
477 return paramInfos
.Length
> 0 &&
478 typeof(IAsyncResult
).IsAssignableFrom(paramInfos
[0].ParameterType
) &&
479 methodInfo
.Name
.StartsWith("End", StringComparison
.Ordinal
);
482 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Create"]/*' />
484 /// <para>[To be supplied.]</para>
486 public static LogicalMethodInfo
[] Create(MethodInfo
[] methodInfos
) {
487 return Create(methodInfos
, LogicalMethodTypes
.Async
| LogicalMethodTypes
.Sync
, null);
491 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Create1"]/*' />
493 /// <para>[To be supplied.]</para>
495 public static LogicalMethodInfo
[] Create(MethodInfo
[] methodInfos
, LogicalMethodTypes types
) {
496 return Create(methodInfos
, types
, null);
499 internal static LogicalMethodInfo
[] Create(MethodInfo
[] methodInfos
, LogicalMethodTypes types
, Hashtable declarations
) {
500 ArrayList begins
= (types
& LogicalMethodTypes
.Async
) != 0 ? new ArrayList() : null;
501 Hashtable ends
= (types
& LogicalMethodTypes
.Async
) != 0 ? new Hashtable() : null;
502 ArrayList syncs
= (types
& LogicalMethodTypes
.Sync
) != 0 ? new ArrayList() : null;
504 for (int i
= 0; i
< methodInfos
.Length
; i
++) {
505 MethodInfo methodInfo
= methodInfos
[i
];
506 if (IsBeginMethod(methodInfo
)) {
507 if (begins
!= null) begins
.Add(methodInfo
);
509 else if (IsEndMethod(methodInfo
)) {
510 if (ends
!= null) ends
.Add(methodInfo
.Name
, methodInfo
);
513 if (syncs
!= null) syncs
.Add(methodInfo
);
517 int beginsCount
= begins
== null ? 0 : begins
.Count
;
518 int syncsCount
= syncs
== null ? 0 : syncs
.Count
;
519 int count
= syncsCount
+ beginsCount
;
520 LogicalMethodInfo
[] methods
= new LogicalMethodInfo
[count
];
522 for (int i
= 0; i
< syncsCount
; i
++) {
523 MethodInfo syncMethod
= (MethodInfo
)syncs
[i
];
524 WebMethod webMethod
= declarations
== null ? null : (WebMethod
)declarations
[syncMethod
];
525 methods
[count
] = new LogicalMethodInfo(syncMethod
, webMethod
);
526 methods
[count
].CheckContractOverride();
529 for (int i
= 0; i
< beginsCount
; i
++) {
530 MethodInfo beginMethodInfo
= (MethodInfo
)begins
[i
];
531 string endName
= "End" + beginMethodInfo
.Name
.Substring(5);
532 MethodInfo endMethodInfo
= (MethodInfo
)ends
[endName
];
533 if (endMethodInfo
== null) {
534 throw new InvalidOperationException(Res
.GetString(Res
.WebAsyncMissingEnd
, beginMethodInfo
.DeclaringType
.FullName
, beginMethodInfo
.Name
, endName
));
536 WebMethod webMethod
= declarations
== null ? null : (WebMethod
)declarations
[beginMethodInfo
];
537 methods
[count
++] = new LogicalMethodInfo(beginMethodInfo
, endMethodInfo
, webMethod
);
543 internal static HashAlgorithm HashAlgorithm
{
546 hash
= SHA1
.Create();
552 internal string GetKey() {
553 if (methodInfo
== null)
555 string key
= methodInfo
.DeclaringType
.FullName
+ ":" + methodInfo
.ToString();
556 // for very long method signatures use a hash string instead of actual method signature.
557 if (key
.Length
> 1024) {
558 byte[] bytes
= HashAlgorithm
.ComputeHash(Encoding
.UTF8
.GetBytes(key
));
559 key
= Convert
.ToBase64String(bytes
);
564 internal void CheckContractOverride() {
565 if (declaration
== null)
567 methodInfo
.GetParameters();
568 ParameterInfo
[] parameters
= methodInfo
.GetParameters();
569 foreach (ParameterInfo p
in parameters
) {
570 object[] attrs
= p
.GetCustomAttributes(false);
571 foreach (object o
in attrs
) {
572 if (o
.GetType().Namespace
== "System.Xml.Serialization") {
573 throw new InvalidOperationException(Res
.GetString(Res
.ContractOverride
, methodInfo
.Name
, methodInfo
.DeclaringType
.FullName
, declaration
.DeclaringType
.FullName
, declaration
.ToString(), o
.ToString()));
579 internal static bool CanMerge(Type type
) {
580 if (type
== typeof(SoapHeaderAttribute
))
582 if (typeof(SoapExtensionAttribute
).IsAssignableFrom(type
))