Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Web.Services / System / Web / Services / Protocols / LogicalMethodInfo.cs
blob1e3cb1b68c44fa5a8375319b18e99fd0f1541846
1 //------------------------------------------------------------------------------
2 // <copyright file="LogicalMethodInfo.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Web.Services.Protocols {
9 using System;
10 using System.Web.Services;
11 using System.Reflection;
12 using System.Collections;
13 using System.Security.Permissions;
14 using System.Globalization;
15 using System.Text;
16 using System.Security.Cryptography;
18 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes"]/*' />
19 /// <devdoc>
20 /// <para>[To be supplied.]</para>
21 /// </devdoc>
22 public enum LogicalMethodTypes {
23 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes.Sync"]/*' />
24 /// <devdoc>
25 /// <para>[To be supplied.]</para>
26 /// </devdoc>
27 Sync = 0x1,
28 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodTypes.Async"]/*' />
29 /// <devdoc>
30 /// <para>[To be supplied.]</para>
31 /// </devdoc>
32 Async = 0x2,
36 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo"]/*' />
37 /// <devdoc>
38 /// <para>[To be supplied.]</para>
39 /// </devdoc>
40 public sealed class LogicalMethodInfo {
41 MethodInfo methodInfo;
42 MethodInfo endMethodInfo;
43 ParameterInfo[] inParams;
44 ParameterInfo[] outParams;
45 ParameterInfo[] parameters;
46 Hashtable attributes;
47 Type retType;
48 ParameterInfo callbackParam;
49 ParameterInfo stateParam;
50 ParameterInfo resultParam;
51 string methodName;
52 bool isVoid;
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"]/*' />
60 /// <devdoc>
61 /// <para>[To be supplied.]</para>
62 /// </devdoc>
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"]/*' />
121 /// <devdoc>
122 /// <para>[To be supplied.]</para>
123 /// </devdoc>
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"]/*' />
130 /// <devdoc>
131 /// <para>[To be supplied.]</para>
132 /// </devdoc>
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];
140 values = newValues;
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];
147 count = 0;
148 if (!isVoid) results[count++] = returnValue;
149 for (int i = 0; i < outParams.Length; i++) {
150 results[count++] = values[outParams[i].Position];
152 return results;
154 else if (isVoid) {
155 return emptyObjectArray;
157 else {
158 return new object[] { returnValue };
162 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.BeginInvoke"]/*' />
163 /// <devdoc>
164 /// <para>[To be supplied.]</para>
165 /// </devdoc>
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"]/*' />
176 /// <devdoc>
177 /// <para>[To be supplied.]</para>
178 /// </devdoc>
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);
184 if (!isVoid) {
185 values[0] = returnValue;
186 return values;
188 else if (outParams.Length > 0) {
189 object[] newValues = new object[outParams.Length];
190 Array.Copy(values, 1, newValues, 0, newValues.Length);
191 return newValues;
193 else {
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"]/*' />
207 /// <devdoc>
208 /// <para>[To be supplied.]</para>
209 /// </devdoc>
210 public Type DeclaringType {
211 get { return methodInfo.DeclaringType; }
214 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Name"]/*' />
215 /// <devdoc>
216 /// <para>[To be supplied.]</para>
217 /// </devdoc>
218 public string Name {
219 get { return methodName; }
222 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncResultParameter"]/*' />
223 /// <devdoc>
224 /// <para>[To be supplied.]</para>
225 /// </devdoc>
226 public ParameterInfo AsyncResultParameter {
227 get { return resultParam; }
230 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncCallbackParameter"]/*' />
231 /// <devdoc>
232 /// <para>[To be supplied.]</para>
233 /// </devdoc>
234 public ParameterInfo AsyncCallbackParameter {
235 get { return callbackParam; }
238 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.AsyncStateParameter"]/*' />
239 /// <devdoc>
240 /// <para>[To be supplied.]</para>
241 /// </devdoc>
242 public ParameterInfo AsyncStateParameter {
243 get { return stateParam; }
246 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.ReturnType"]/*' />
247 /// <devdoc>
248 /// <para>[To be supplied.]</para>
249 /// </devdoc>
250 public Type ReturnType {
251 get { return retType; }
254 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsVoid"]/*' />
255 /// <devdoc>
256 /// <para>[To be supplied.]</para>
257 /// </devdoc>
258 public bool IsVoid {
259 get { return isVoid; }
262 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.IsAsync"]/*' />
263 /// <devdoc>
264 /// <para>[To be supplied.]</para>
265 /// </devdoc>
266 public bool IsAsync {
267 get { return endMethodInfo != null; }
270 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.InParameters"]/*' />
271 /// <devdoc>
272 /// <para>[To be supplied.]</para>
273 /// </devdoc>
274 public ParameterInfo[] InParameters {
275 get { return inParams; }
278 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.OutParameters"]/*' />
279 /// <devdoc>
280 /// <para>[To be supplied.]</para>
281 /// </devdoc>
282 public ParameterInfo[] OutParameters {
283 get { return outParams; }
286 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.Parameters"]/*' />
287 /// <devdoc>
288 /// <para>[To be supplied.]</para>
289 /// </devdoc>
290 public ParameterInfo[] Parameters {
291 get { return parameters; }
294 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.GetCustomAttributes"]/*' />
295 /// <devdoc>
296 /// <para>[To be supplied.]</para>
297 /// </devdoc>
298 public object[] GetCustomAttributes(Type type) {
299 object[] attrForType = null;
300 attrForType = (object[])attributes[type];
301 if (attrForType != null)
302 return attrForType;
303 lock (attributes) {
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);
320 else {
321 throw new InvalidOperationException(Res.GetString(Res.ContractOverride, methodInfo.Name, methodInfo.DeclaringType.FullName, declaration.DeclaringType.FullName, declaration.ToString(), implAttributes[0].ToString()));
324 else {
325 attrForType = declAttributes;
328 else {
329 attrForType = methodInfo.GetCustomAttributes(type, false);
331 attributes[type] = attrForType;
334 return attrForType;
338 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.GetCustomAttribute"]/*' />
339 /// <devdoc>
340 /// <para>[To be supplied.]</para>
341 /// </devdoc>
342 public object GetCustomAttribute(Type type) {
343 object[] attrs = GetCustomAttributes(type);
344 if (attrs.Length == 0) return null;
345 return attrs[0];
348 internal WebMethodAttribute MethodAttribute {
349 get {
350 if (attribute == null) {
351 attribute = (WebMethodAttribute)GetCustomAttribute(typeof(WebMethodAttribute));
352 if (attribute == null) {
353 attribute = new WebMethodAttribute();
356 return attribute;
360 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.CustomAttributeProvider"]/*' />
361 /// <devdoc>
362 /// <para>[To be supplied.]</para>
363 /// </devdoc>
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"]/*' />
370 /// <devdoc>
371 /// <para>[To be supplied.]</para>
372 /// </devdoc>
373 public ICustomAttributeProvider ReturnTypeCustomAttributeProvider {
374 get {
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"]/*' />
385 /// <devdoc>
386 /// <para>[To be supplied.]</para>
387 /// </devdoc>
388 public MethodInfo MethodInfo {
389 get { return endMethodInfo == null ? methodInfo : null; }
392 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.BeginMethodInfo"]/*' />
393 /// <devdoc>
394 /// <para>[To be supplied.]</para>
395 /// </devdoc>
396 public MethodInfo BeginMethodInfo {
397 get { return methodInfo; }
400 /// <include file='doc\LogicalMethodInfo.uex' path='docs/doc[@for="LogicalMethodInfo.EndMethodInfo"]/*' />
401 /// <devdoc>
402 /// <para>[To be supplied.]</para>
403 /// </devdoc>
404 public MethodInfo EndMethodInfo {
405 get { return endMethodInfo; }
408 static ParameterInfo[] GetInParameters(MethodInfo methodInfo, ParameterInfo[] paramInfos, int start, int length, bool mustBeIn) {
409 int count = 0;
410 for (int i = 0; i < length; i++) {
411 ParameterInfo paramInfo = paramInfos[i + start];
412 if (IsInParameter(paramInfo)) {
413 count++;
415 else if (mustBeIn) {
416 throw new InvalidOperationException(Res.GetString(Res.WebBadOutParameter, paramInfo.Name, methodInfo.DeclaringType.FullName, paramInfo.Name));
420 ParameterInfo[] ins = new ParameterInfo[count];
421 count = 0;
422 for (int i = 0; i < length; i++) {
423 ParameterInfo paramInfo = paramInfos[i + start];
424 if (IsInParameter(paramInfo)) {
425 ins[count++] = paramInfo;
428 return ins;
431 static ParameterInfo[] GetOutParameters(MethodInfo methodInfo, ParameterInfo[] paramInfos, int start, int length, bool mustBeOut) {
432 int count = 0;
433 for (int i = 0; i < length; i++) {
434 ParameterInfo paramInfo = paramInfos[i + start];
435 if (IsOutParameter(paramInfo)) {
436 count++;
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];
444 count = 0;
445 for (int i = 0; i < length; i++) {
446 ParameterInfo paramInfo = paramInfos[i + start];
447 if (IsOutParameter(paramInfo)) {
448 outs[count++] = paramInfo;
451 return outs;
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"]/*' />
463 /// <devdoc>
464 /// <para>[To be supplied.]</para>
465 /// </devdoc>
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"]/*' />
472 /// <devdoc>
473 /// <para>[To be supplied.]</para>
474 /// </devdoc>
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"]/*' />
483 /// <devdoc>
484 /// <para>[To be supplied.]</para>
485 /// </devdoc>
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"]/*' />
492 /// <devdoc>
493 /// <para>[To be supplied.]</para>
494 /// </devdoc>
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);
512 else {
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];
521 count = 0;
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();
527 count++;
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);
540 return methods;
543 internal static HashAlgorithm HashAlgorithm {
544 get {
545 if (hash == null) {
546 hash = SHA1.Create();
548 return hash;
552 internal string GetKey() {
553 if (methodInfo == null)
554 return string.Empty;
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);
561 return key;
564 internal void CheckContractOverride() {
565 if (declaration == null)
566 return;
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))
581 return true;
582 if (typeof(SoapExtensionAttribute).IsAssignableFrom(type))
583 return true;
584 return false;