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))