[wasm] Implement GC support. Embedder must provide main loop pumping function request...
[mono.git] / mcs / class / System.Web.Extensions / System.Web.Script.Services / LogicalTypeInfo.cs
1 //
2 // LogicalTypeInfo.cs
3 //
4 // Author:
5 //   Konstantin Triger <kostat@mainsoft.com>
6 //   Atsushi Enomoto <atsushi@ximian.com>
7 //
8 // (C) 2007 Mainsoft, Inc.  http://www.mainsoft.com
9 // Copyright (C) 2009 Novell, Inc. http://novell.com
10 //
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections.Generic;
34 using System.Collections.Specialized;
35 using System.Text;
36 using System.Web.Services;
37 using System.Reflection;
38 using System.Collections;
39 using System.Web.Script.Serialization;
40 using System.IO;
41 using System.Xml.Serialization;
42 using System.Xml;
43 using System.ServiceModel;
44 using System.ServiceModel.Description;
45
46 namespace System.Web.Script.Services
47 {
48         internal sealed class JsonResult
49         {
50                 public readonly object d;
51                 public JsonResult (object result) {
52                         d = result;
53                 }
54         }
55
56         internal abstract class LogicalTypeInfo
57         {
58                 public static LogicalTypeInfo CreateTypeInfo (Type t, string filePath)
59                 {
60                         if (t.GetCustomAttributes (typeof (ServiceContractAttribute), false).Length > 0)
61                                 return new WcfLogicalTypeInfo (t, filePath);
62                         else
63                                 return new AsmxLogicalTypeInfo (t, filePath);
64                 }
65
66                 internal abstract class LogicalMethodInfo
67                 {
68                         readonly MethodInfo _methodInfo;
69                         internal readonly ParameterInfo [] _params;
70                         internal readonly Dictionary<string, int> _paramMap;
71                         LogicalTypeInfo _typeInfo;
72
73                         protected LogicalMethodInfo (LogicalTypeInfo typeInfo, MethodInfo method)
74                         {
75                                 _methodInfo = method;
76                                 _params = MethodInfo.GetParameters ();
77                                 _typeInfo = typeInfo;
78
79                                 if (HasParameters) {
80                                         _paramMap = new Dictionary<string, int> (_params.Length, StringComparer.Ordinal);
81                                         for (int i = 0; i < _params.Length; i++)
82                                                 _paramMap.Add(_params[i].Name, i);
83                                 }
84
85                         }
86
87                         public abstract bool UseHttpGet { get; }
88                         public abstract bool EnableSession { get; }
89                         public abstract ResponseFormat ResponseFormat { get; }
90                         public abstract string MethodName { get; }
91                         public MethodInfo MethodInfo { get { return _methodInfo; } }
92                         public bool HasParameters { get { return _params != null && _params.Length > 0; } }
93                         public IEnumerable<Type> GetParameterTypes () {
94                                 if (HasParameters)
95                                         for (int i = 0; i < _params.Length; i++)
96                                                 yield return _params [i].ParameterType;
97
98                                 yield return MethodInfo.ReturnType;
99                         }
100
101                         public void GenerateMethod (StringBuilder proxy, bool isPrototype, bool isPage) {
102                                 string ns;
103                                 string service;// = isPage ? "PageMethods" : MethodInfo.DeclaringType.FullName;
104
105                                 _typeInfo.GetNamespaceAndServiceName (MethodInfo.DeclaringType, isPage, out ns, out service);
106                                 string useHttpGet = UseHttpGet ? "true" : "false";
107                                 string paramMap = GenerateParameters (true);
108                                 string paramList = GenerateParameters (false);
109
110                                 if (isPrototype){
111                                         proxy.AppendFormat (
112 @"
113 {1}:function({4}succeededCallback, failedCallback, userContext) {{
114 return this._invoke({0}.get_path(), '{1}',{2},{{{3}}},succeededCallback,failedCallback,userContext); }}",
115                                         service, MethodName, useHttpGet, paramMap, paramList);
116                                 }
117                                 else
118                                         proxy.AppendFormat (
119 @"
120 {0}.{1}= function({2}onSuccess,onFailed,userContext) {{{0}._staticInstance.{1}({2}onSuccess,onFailed,userContext); }}",
121                                         service, MethodName, paramList);
122                         }
123
124                         string GenerateParameters (bool isMap) {
125                                 if (!HasParameters)
126                                         return null;
127
128                                 StringBuilder builder = new StringBuilder ();
129
130                                 for (int i = 0; i < _params.Length; i++) {
131                                         builder.AppendFormat (isMap ? "{0}:{0}" : "{0}", _params [i].Name);
132                                         builder.Append (',');
133                                 }
134
135                                 if (isMap)
136                                         builder.Length--;
137
138                                 return builder.ToString ();
139                         }
140
141                         public abstract void Invoke (HttpRequest request, HttpResponse response);
142                 }
143
144                 static Hashtable _type_to_logical_type = Hashtable.Synchronized (new Hashtable ());
145
146                 static internal LogicalTypeInfo GetLogicalTypeInfo (Type t, string filePath) {
147                         Hashtable type_to_manager = _type_to_logical_type;
148                         LogicalTypeInfo tm = (LogicalTypeInfo) type_to_manager [t];
149
150                         if (tm != null)
151                                 return tm;
152
153                         tm = CreateTypeInfo (t, filePath);
154                         type_to_manager [t] = tm;
155
156                         return tm;
157                 }
158
159                 protected static string EnsureNamespaceRegistered (string ns, string name, StringBuilder proxy, List<string> registeredNamespaces) {
160                         if (String.IsNullOrEmpty (ns))
161                                 return "var " + name;
162
163                         if (!registeredNamespaces.Contains (ns)) {
164                                 registeredNamespaces.Add (ns);
165                                 proxy.AppendFormat (
166 @"
167 Type.registerNamespace('{0}');",
168                                                                    ns);
169                         }
170                         return name;
171                 }
172
173                 protected virtual void GetNamespaceAndServiceName (Type type, bool isPage, out string ns, out string service)
174                 {
175                         ns = isPage ? String.Empty : type.Namespace;
176                         service = isPage ? "PageMethods" : type.FullName;
177                 }
178
179                 // instance members
180
181                 internal readonly Type _type;
182                 readonly string _proxy;
183                 internal readonly Hashtable _methodMap;
184
185                 protected LogicalTypeInfo (Type t, string filePath)
186                 {
187                         _type = t;
188                         bool isPage = _type.IsSubclassOf (typeof (System.Web.UI.Page));
189
190                         var logicalMethods = GetLogicalMethods (isPage);
191                         //_logicalMethods = (LogicalMethodInfo []) list.ToArray (typeof (LogicalMethodInfo));
192
193                         _methodMap = new Hashtable (logicalMethods.Count);
194                         for (int i = 0; i < logicalMethods.Count; i++)
195                                 _methodMap.Add (logicalMethods [i].MethodName, logicalMethods [i]);
196
197                         string ns;
198                         string service;
199                         GetNamespaceAndServiceName (t, isPage, out ns, out service);
200                         
201                         StringBuilder proxy = new StringBuilder ();
202                         List<string> registeredNamespaces = new List<string> ();
203                         string scriptTypeDeclaration = EnsureNamespaceRegistered (ns, service, proxy, registeredNamespaces);
204                         proxy.AppendFormat (
205 @"
206 " + scriptTypeDeclaration + @"=function() {{
207 {0}.initializeBase(this);
208 this._timeout = 0;
209 this._userContext = null;
210 this._succeeded = null;
211 this._failed = null;
212 }}
213 {0}.prototype={{",
214                         service);
215
216                         for (int i = 0; i < logicalMethods.Count; i++) {
217                                 if (i > 0)
218                                         proxy.Append (',');
219                                 logicalMethods [i].GenerateMethod (proxy, true, isPage);
220                         }
221
222                         proxy.AppendFormat (
223 @"}}
224 {0}.registerClass('{0}',Sys.Net.WebServiceProxy);
225 {0}._staticInstance = new {0}();
226 {0}.set_path = function(value) {{ {0}._staticInstance.set_path(value); }}
227 {0}.get_path = function() {{ return {0}._staticInstance.get_path(); }}
228 {0}.set_timeout = function(value) {{ {0}._staticInstance.set_timeout(value); }}
229 {0}.get_timeout = function() {{ return {0}._staticInstance.get_timeout(); }}
230 {0}.set_defaultUserContext = function(value) {{ {0}._staticInstance.set_defaultUserContext(value); }}
231 {0}.get_defaultUserContext = function() {{ return {0}._staticInstance.get_defaultUserContext(); }}
232 {0}.set_defaultSucceededCallback = function(value) {{ {0}._staticInstance.set_defaultSucceededCallback(value); }}
233 {0}.get_defaultSucceededCallback = function() {{ return {0}._staticInstance.get_defaultSucceededCallback(); }}
234 {0}.set_defaultFailedCallback = function(value) {{ {0}._staticInstance.set_defaultFailedCallback(value); }}
235 {0}.get_defaultFailedCallback = function() {{ return {0}._staticInstance.get_defaultFailedCallback(); }}
236 {0}.set_path(""{1}"");",
237                         service, filePath);
238
239                         for (int i = 0; i < logicalMethods.Count; i++)
240                                 logicalMethods [i].GenerateMethod (proxy, false, isPage);
241
242                         GenerateTypeRegistrationScript (proxy, registeredNamespaces);
243
244                         proxy.AppendLine ();
245                         _proxy = proxy.ToString ();
246                 }
247
248                 protected IEnumerable<MemberInfo> GetGenerateScriptTypes () {
249                         foreach (LogicalMethodInfo lmi in _methodMap.Values)
250                                 yield return lmi.MethodInfo;
251
252                         yield return _type;
253                 }
254
255                 protected static void GenerateTypeRegistrationScript (StringBuilder proxy, Type scriptType, string scriptTypeId, List<string> registeredNamespaces) {
256                         string className = scriptType.FullName.Replace ('+', '_');
257                         string ns = scriptType.Namespace;
258                         string scriptTypeDeclaration = EnsureNamespaceRegistered (ns, className, proxy, registeredNamespaces);
259                         proxy.AppendFormat (
260 @"
261 if (typeof({0}) === 'undefined') {{", className);
262                         if (scriptType.IsEnum) {
263                                 proxy.AppendFormat (
264 @"
265 {0} = function() {{ throw Error.invalidOperation(); }}
266 {0}.prototype = {1}
267 {0}.registerEnum('{0}', {2});",
268                                 className,
269                                 // This method is also used for WCF, but for enum this should work ...
270                                 AsmxLogicalTypeInfo.JSSerializer.Serialize(GetEnumPrototypeDictionary (scriptType)),
271                                 Attribute.GetCustomAttribute (scriptType, typeof (FlagsAttribute)) != null ? "true" : "false");
272                                 
273                         }
274                         else {
275                                 string typeId = String.IsNullOrEmpty (scriptTypeId) ? scriptType.FullName : scriptTypeId;
276                                 proxy.AppendFormat (
277 @"
278 " + scriptTypeDeclaration + @"=gtc(""{1}"");
279 {0}.registerClass('{0}');",
280                                 className, typeId);
281                         }
282                         proxy.Append ('}');
283                 }
284
285                 static IDictionary <string, object> GetEnumPrototypeDictionary (Type type)
286                 {
287                         var ret = new Dictionary <string, object> ();
288                         string [] names = Enum.GetNames (type);
289                         Array values = Enum.GetValues (type);
290                         for (int i = 0; i < names.Length; i++)
291                                 ret.Add (names [i], values.GetValue (i));
292
293                         return ret;
294                 }
295
296                 static readonly Type typeOfIEnumerable = typeof (IEnumerable);
297                 static readonly Type typeOfIDictionary = typeof (IDictionary);
298
299                 protected static bool ShouldGenerateScript (Type type, bool throwIfNot) {
300                         if (type.IsEnum)
301                                 return true;
302
303                         if (Type.GetTypeCode (type) != TypeCode.Object)
304                                 return false;
305
306                         if (type == typeof (void))
307                                 return false;
308
309                         if (typeOfIEnumerable.IsAssignableFrom (type) ||
310                                 typeOfIDictionary.IsAssignableFrom (type) ||
311                                 type.IsAbstract || type.IsInterface) {
312                                 if (throwIfNot)
313                                         ThrowOnIncorrectGenerateScriptAttribute ();
314                                 return false;
315                         }
316
317                         // LAMESPEC: MS never create proxies for GenericTypes
318                         //&& type.GetGenericTypeDefinition ().GetGenericArguments ().Length > 1
319                         if (type.IsGenericType)
320                                 return false;
321
322                         ConstructorInfo ci = type.GetConstructor (Type.EmptyTypes);
323                         if (ci == null || !ci.IsPublic) {
324                                 if (throwIfNot)
325                                         ThrowOnIncorrectGenerateScriptAttribute ();
326                                 return false;
327                         }
328
329                         return true;
330                 }
331
332                 static void ThrowOnIncorrectGenerateScriptAttribute () {
333                         throw new InvalidOperationException (
334                                 "Using the GenerateScriptTypes attribute is not supported for types in the following categories: primitive types; DateTime; generic types taking more than one parameter; types implementing IEnumerable or IDictionary; interfaces; Abstract classes; classes without a public default constructor.");
335                 }
336                 
337                 protected abstract void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces);
338
339                 protected abstract List<LogicalMethodInfo> GetLogicalMethods (bool isPage);
340
341                 public string Proxy { get { return _proxy; } }
342
343                 public LogicalMethodInfo this [string method] {
344                         get { return (LogicalMethodInfo) _methodMap [method]; }
345                 }
346         }
347
348         internal sealed class AsmxLogicalTypeInfo : LogicalTypeInfo
349         {
350                 #region LogicalMethodInfo
351
352                 public sealed class AsmxLogicalMethodInfo : LogicalTypeInfo.LogicalMethodInfo
353                 {
354                         readonly LogicalTypeInfo _typeInfo;
355
356                         readonly WebMethodAttribute _wma;
357
358                         readonly ScriptMethodAttribute _sma;
359
360                         readonly XmlSerializer _xmlSer;
361
362                         public AsmxLogicalMethodInfo (LogicalTypeInfo typeInfo, MethodInfo method)
363                                 : base (typeInfo, method)
364                         {
365                                 _typeInfo = typeInfo;
366
367                                 _wma = (WebMethodAttribute) Attribute.GetCustomAttribute (method, typeof (WebMethodAttribute));
368
369                                 _sma = (ScriptMethodAttribute) Attribute.GetCustomAttribute (method, typeof (ScriptMethodAttribute));
370                                 if (_sma == null)
371                                         _sma = ScriptMethodAttribute.Default;
372
373                                 if (ScriptMethod.ResponseFormat == ResponseFormat.Xml
374                                         && MethodInfo.ReturnType != typeof (void)) {
375                                         Type retType = MethodInfo.ReturnType;
376                                         if (Type.GetTypeCode (retType) != TypeCode.String || ScriptMethod.XmlSerializeString)
377                                                 _xmlSer = new XmlSerializer (retType);
378                                 }
379                         }
380
381                         IDictionary<string,object> BuildInvokeParameters (HttpRequest request)
382                         {
383                                 return "GET".Equals (request.RequestType, StringComparison.OrdinalIgnoreCase) ?
384                                         GetNameValueCollectionDictionary (request.QueryString) :
385                                         (IDictionary<string, object>) JavaScriptSerializer.DefaultSerializer.DeserializeObjectInternal (new StreamReader (request.InputStream, request.ContentEncoding));
386                         }
387
388                         IDictionary <string, object> GetNameValueCollectionDictionary (NameValueCollection nvc)
389                         {
390                                 var ret = new Dictionary <string, object> ();
391
392                                 for (int i = nvc.Count - 1; i >= 0; i--)
393                                         ret.Add (nvc.GetKey (i), nvc.Get (i));
394
395                                 return ret;
396                         }
397
398                         public override void Invoke (HttpRequest request, HttpResponse response) {
399                                 var writer = response.Output;
400                                 IDictionary<string, object> @params = BuildInvokeParameters (request);
401
402                                 object [] pp = null;
403                                 if (HasParameters) {
404                                         Type ptype;
405                                         int i;
406                                         object value;
407                                         pp = new object [_params.Length];
408
409                                         foreach (KeyValuePair<string, object> pair in @params) {
410                                                 if (!_paramMap.TryGetValue (pair.Key, out i))
411                                                         continue;
412
413                                                 value = pair.Value;
414                                                 ptype = _params [i].ParameterType;
415                                                 if (ptype == typeof (System.Object))
416                                                         pp [i] = value;
417                                                 else
418                                                         pp [i] = AsmxLogicalTypeInfo.JSSerializer.ConvertToType (value, ptype);
419                                         }
420                                 }
421
422                                 object target = MethodInfo.IsStatic ? null : Activator.CreateInstance (_typeInfo._type);
423                                 object result = MethodInfo.Invoke (target, pp);
424                                 if (_xmlSer != null) {
425                                         XmlTextWriter xwriter = new XmlTextWriter (writer);
426                                         xwriter.Formatting = Formatting.None;
427                                         _xmlSer.Serialize (xwriter, result);
428                                 }
429                                 else
430                                 {
431                                         result = new JsonResult (result);
432                                         AsmxLogicalTypeInfo.JSSerializer.Serialize (result, writer);
433                                 }
434                         }
435
436                         public override string MethodName { get { return String.IsNullOrEmpty (WebMethod.MessageName) ? MethodInfo.Name : WebMethod.MessageName; } }
437
438                         public ScriptMethodAttribute ScriptMethod { get { return _sma; } }
439                         public WebMethodAttribute WebMethod { get { return _wma; } }
440                         public override bool UseHttpGet { get { return ScriptMethod.UseHttpGet; } }
441                         public override bool EnableSession { get { return WebMethod.EnableSession; } }
442                         public override ResponseFormat ResponseFormat { get { return ScriptMethod.ResponseFormat; } }
443                 }
444
445                 #endregion
446
447                 //readonly LogicalMethodInfo [] _logicalMethods;
448                 internal static readonly JavaScriptSerializer JSSerializer = new JavaScriptSerializer (null, true);
449
450                 protected override List<LogicalMethodInfo> GetLogicalMethods (bool isPage)
451                 {
452                         BindingFlags bindingAttr = isPage ? (BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
453                         MethodInfo [] all_type_methods = _type.GetMethods (bindingAttr);
454                         List<LogicalMethodInfo> logicalMethods = new List<LogicalMethodInfo> (all_type_methods.Length);
455                         foreach (MethodInfo mi in all_type_methods) {
456                                 if (mi.IsPublic && 
457                                         mi.GetCustomAttributes (typeof (WebMethodAttribute), false).Length > 0)
458                                         logicalMethods.Add (new AsmxLogicalMethodInfo (this, mi));
459                                 else {
460                                         foreach (Type ifaceType in _type.GetInterfaces ()) {
461                                                 if (ifaceType.GetCustomAttributes (typeof (WebServiceBindingAttribute), false).Length > 0) {
462                                                         MethodInfo found = FindInInterface (ifaceType, mi);
463                                                         if (found != null) {
464                                                                 if (found.GetCustomAttributes (typeof (WebMethodAttribute), false).Length > 0)
465                                                                         logicalMethods.Add (new AsmxLogicalMethodInfo (this, found));
466
467                                                                 break;
468                                                         }
469                                                 }
470                                         }
471                                 }
472                         }
473                         return logicalMethods;
474                 }
475
476                 internal AsmxLogicalTypeInfo (Type t, string filePath) 
477                         : base (t, filePath)
478                 {
479                 }
480
481                 IEnumerable<GenerateScriptTypeAttribute> GetGenerateScriptTypeAttributes () {
482                         Hashtable generatedTypes = new Hashtable ();
483
484                         foreach (MemberInfo mi in GetGenerateScriptTypes ()) {
485                                 GenerateScriptTypeAttribute [] gstas = (GenerateScriptTypeAttribute []) mi.GetCustomAttributes (typeof (GenerateScriptTypeAttribute), true);
486                                 if (gstas == null || gstas.Length == 0)
487                                         continue;
488
489                                 for (int i = 0; i < gstas.Length; i++) {
490                                         if (!generatedTypes.Contains (gstas [i].Type)) {
491                                                 if (ShouldGenerateScript (gstas [i].Type, true)) {
492                                                         generatedTypes [gstas [i].Type] = gstas [i].Type;
493                                                         yield return gstas [i];
494                                                 }
495                                         }
496                                 }
497                         }
498
499                         foreach (LogicalMethodInfo lmi in _methodMap.Values) {
500                                 foreach (Type t in lmi.GetParameterTypes ()) {
501                                         Type param = GetTypeToGenerate (t);
502                                         if (!generatedTypes.Contains (param)) {
503                                                 if (ShouldGenerateScript (param, false)) {
504                                                         generatedTypes [param] = param;
505                                                         yield return new GenerateScriptTypeAttribute (param);
506                                                 }
507                                         }
508                                 }
509                         }
510                 }
511
512                 static Type GetTypeToGenerate (Type type) {
513                         if (type.IsArray)
514                                 return type.GetElementType ();
515                         if (type.IsGenericType) {
516                                 while (type.IsGenericType && type.GetGenericArguments ().Length == 1)
517                                         type = type.GetGenericArguments () [0];
518                                 return type;
519                         }
520                         return type;
521                 }
522
523                 static MethodInfo FindInInterface (Type ifaceType, MethodInfo method) {
524                         int nameStartIndex = 0;
525                         if (method.IsPrivate) {
526                                 nameStartIndex = method.Name.LastIndexOf ('.');
527                                 if (nameStartIndex < 0)
528                                         nameStartIndex = 0;
529                                 else {
530                                         if (String.CompareOrdinal (
531                                                 ifaceType.FullName.Replace ('+', '.'), 0, method.Name, 0, nameStartIndex) != 0)
532                                                 return null;
533
534                                         nameStartIndex++;
535                                 }
536                         }
537                         foreach (MethodInfo mi in ifaceType.GetMembers ()) {
538                                 if (method.ReturnType == mi.ReturnType &&
539                                         String.CompareOrdinal (method.Name, nameStartIndex, mi.Name, 0, mi.Name.Length) == 0) {
540                                         ParameterInfo [] rpi = method.GetParameters ();
541                                         ParameterInfo [] lpi = mi.GetParameters ();
542                                         if (rpi.Length == lpi.Length) {
543                                                 bool match = true;
544                                                 for (int i = 0; i < rpi.Length; i++) {
545                                                         if (rpi [i].ParameterType != lpi [i].ParameterType) {
546                                                                 match = false;
547                                                                 break;
548                                                         }
549                                                 }
550
551                                                 if (match)
552                                                         return mi;
553                                         }
554                                 }
555                         }
556
557                         return null;
558                 }
559
560                 protected override void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces)
561                 {
562                         bool gtc = false;
563
564                         foreach (GenerateScriptTypeAttribute gsta in GetGenerateScriptTypeAttributes ()) {
565                                 if (!gtc && !gsta.Type.IsEnum) {
566                                         proxy.Append (
567 @"
568 var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;");
569                                         gtc = true;
570                                 }
571                                 GenerateTypeRegistrationScript (proxy, gsta.Type, gsta.ScriptTypeId, registeredNamespaces);
572                         }
573                 }
574         }
575
576         internal class WcfLogicalTypeInfo : LogicalTypeInfo
577         {
578                 ContractDescription cd;
579
580                 public WcfLogicalTypeInfo (Type type, string filePath)
581                         : base (type, filePath)
582                 {
583                 }
584
585                 ContractDescription Contract {
586                         get {
587                                 if (cd == null)
588                                         cd = ContractDescription.GetContract (_type);
589                                 return cd;
590                         }
591                 }
592
593                 IEnumerable<KeyValuePair<Type,string>> GetDataContractTypeInfos ()
594                 {
595                         foreach (var od in Contract.Operations) {
596                                 foreach (var md in od.Messages) {
597                                         foreach (var pd in md.Body.Parts) {
598                                                 if (ShouldGenerateScript (pd.Type, false))
599                                                         yield return new KeyValuePair<Type,string> (pd.Type, null);
600                                         }
601                                         if (md.Body.ReturnValue != null && ShouldGenerateScript (md.Body.ReturnValue.Type, false))
602                                                 yield return new KeyValuePair<Type,string> (md.Body.ReturnValue.Type, null);
603                                 }
604                         }
605                         yield break;
606                 }
607
608                 protected override void GetNamespaceAndServiceName (Type type, bool isPage, out string ns, out string service)
609                 {
610                         string name = type.Namespace;
611                         int dot = name.LastIndexOf ('.');
612                         if (dot > -1)
613                                 name = name.Substring (dot + 1);
614                         ns = name;
615                         service = name + "." + type.Name;
616                 }
617
618                 protected override void GenerateTypeRegistrationScript (StringBuilder proxy, List<string> registeredNamespaces)
619                 {
620                         bool gtc = false;
621
622                         foreach (KeyValuePair<Type,string> pair in GetDataContractTypeInfos ()) {
623                                 if (!gtc && !pair.Key.IsEnum) {
624                                         proxy.Append (
625 @"
626 var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;");
627                                         gtc = true;
628                                 }
629                                 GenerateTypeRegistrationScript (proxy, pair.Key, pair.Value, registeredNamespaces);
630                         }
631                 }
632
633                 protected override List<LogicalMethodInfo> GetLogicalMethods (bool isPage)
634                 {
635                         if (isPage)
636                                 throw new NotSupportedException ();
637
638                         var l = new List<LogicalMethodInfo> ();
639                         foreach (var od in Contract.Operations)
640                                 l.Add (new WcfLogicalMethodInfo (this, od));
641                         return l;
642                 }
643
644                 internal class WcfLogicalMethodInfo : LogicalMethodInfo
645                 {
646                         OperationDescription od;
647
648                         public WcfLogicalMethodInfo (LogicalTypeInfo typeInfo, OperationDescription od)
649                                 : base (typeInfo, od.SyncMethod)
650                         {
651                                 this.od = od;
652                         }
653
654                         public override bool UseHttpGet { get { return true; } } // always
655
656                         // FIXME: could this be enabled?
657                         public override bool EnableSession {
658                                 get { return false; }
659                         }
660
661                         public override ResponseFormat ResponseFormat {
662                                 get { return ResponseFormat.Json; } // always
663                         }
664
665                         public override string MethodName {
666                                 get { return od.Name; }
667                         }
668
669                         public override void Invoke (HttpRequest request, HttpResponse response)
670                         {
671                                 // invocation is done in WCF part.
672                                 throw new NotSupportedException ();
673                         }
674                 }
675         }
676 }