1 
2 //
3 // Copyright (c) ZeroC, Inc. All rights reserved.
4 //
5 
6 #include <CsUtil.h>
7 #include <DotNetNames.h>
8 #include <Slice/Util.h>
9 #include <IceUtil/Functional.h>
10 #include <IceUtil/StringUtil.h>
11 
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 
15 #ifdef _WIN32
16 #  include <direct.h>
17 #else
18 #  include <unistd.h>
19 #endif
20 
21 using namespace std;
22 using namespace Slice;
23 using namespace IceUtil;
24 using namespace IceUtilInternal;
25 
26 namespace
27 {
28 
29 string
lookupKwd(const string & name,int baseTypes,bool mangleCasts=false)30 lookupKwd(const string& name, int baseTypes, bool mangleCasts = false)
31 {
32     //
33     // Keyword list. *Must* be kept in alphabetical order.
34     //
35     static const string keywordList[] =
36     {
37         "abstract", "as", "async", "await", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const",
38         "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern",
39         "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface",
40         "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override",
41         "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short",
42         "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof",
43         "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while"
44     };
45     bool found = binary_search(&keywordList[0],
46                                &keywordList[sizeof(keywordList) / sizeof(*keywordList)],
47                                name,
48                                Slice::CICompare());
49     if(found)
50     {
51         return "@" + name;
52     }
53     if(mangleCasts && (name == "checkedCast" || name == "uncheckedCast"))
54     {
55         return string(DotNet::manglePrefix) + name;
56     }
57     return Slice::DotNet::mangleName(name, baseTypes);
58 }
59 
60 //
61 // Split a scoped name into its components and return the components as a list of (unscoped) identifiers.
62 //
63 StringList
splitScopedName(const string & scoped)64 splitScopedName(const string& scoped)
65 {
66     assert(scoped[0] == ':');
67     StringList ids;
68     string::size_type next = 0;
69     string::size_type pos;
70     while((pos = scoped.find("::", next)) != string::npos)
71     {
72         pos += 2;
73         if(pos != scoped.size())
74         {
75             string::size_type endpos = scoped.find("::", pos);
76             if(endpos != string::npos)
77             {
78                 ids.push_back(scoped.substr(pos, endpos - pos));
79             }
80         }
81         next = pos;
82     }
83     if(next != scoped.size())
84     {
85         ids.push_back(scoped.substr(next));
86     }
87     else
88     {
89         ids.push_back("");
90     }
91 
92     return ids;
93 }
94 
95 }
96 
97 string
getNamespacePrefix(const ContainedPtr & cont)98 Slice::CsGenerator::getNamespacePrefix(const ContainedPtr& cont)
99 {
100     //
101     // Traverse to the top-level module.
102     //
103     ModulePtr m;
104     ContainedPtr p = cont;
105     while(true)
106     {
107         if(ModulePtr::dynamicCast(p))
108         {
109             m = ModulePtr::dynamicCast(p);
110         }
111 
112         ContainerPtr c = p->container();
113         p = ContainedPtr::dynamicCast(c); // This cast fails for Unit.
114         if(!p)
115         {
116             break;
117         }
118     }
119 
120     assert(m);
121 
122     static const string prefix = "cs:namespace:";
123 
124     string q;
125     if(m->findMetaData(prefix, q))
126     {
127         q = q.substr(prefix.size());
128     }
129     return q;
130 }
131 
132 string
getCustomTypeIdNamespace(const UnitPtr & ut)133 Slice::CsGenerator::getCustomTypeIdNamespace(const UnitPtr& ut)
134 {
135     DefinitionContextPtr dc = ut->findDefinitionContext(ut->topLevelFile());
136     assert(dc);
137 
138     static const string typeIdNsPrefix = "cs:typeid-namespace:";
139     string result = dc->findMetaData(typeIdNsPrefix);
140     if(!result.empty())
141     {
142         result = result.substr(typeIdNsPrefix.size());
143     }
144     return result;
145 }
146 
147 string
getNamespace(const ContainedPtr & cont)148 Slice::CsGenerator::getNamespace(const ContainedPtr& cont)
149 {
150     string scope = fixId(cont->scope());
151     if(scope.rfind(".") == scope.size() - 1)
152     {
153         scope = scope.substr(0, scope.size() - 1);
154     }
155     string prefix = getNamespacePrefix(cont);
156     if(!prefix.empty())
157     {
158         if(!scope.empty())
159         {
160             return prefix + "." + scope;
161         }
162         else
163         {
164             return prefix;
165         }
166     }
167 
168     return scope;
169 }
170 
171 string
getUnqualified(const string & type,const string & scope,bool builtin)172 Slice::CsGenerator::getUnqualified(const string& type, const string& scope, bool builtin)
173 {
174     if(type.find(".") != string::npos && type.find(scope) == 0 && type.find(".", scope.size() + 1) == string::npos)
175     {
176         return type.substr(scope.size() + 1);
177     }
178     else if(builtin)
179     {
180         return type.find(".") == string::npos ? type : "global::" + type;
181     }
182     else
183     {
184         return "global::" + type;
185     }
186 }
187 
188 string
getUnqualified(const ContainedPtr & p,const string & package,const string & prefix,const string & suffix)189 Slice::CsGenerator::getUnqualified(const ContainedPtr& p, const string& package, const string& prefix,
190                                    const string& suffix)
191 {
192     string name = fixId(prefix + p->name() + suffix);
193     string contPkg = getNamespace(p);
194     if(contPkg == package || contPkg.empty())
195     {
196         return name;
197     }
198     else
199     {
200         return "global::" + contPkg + "." + name;
201     }
202 }
203 
204 //
205 // If the passed name is a scoped name, return the identical scoped name,
206 // but with all components that are C# keywords replaced by
207 // their "@"-prefixed version; otherwise, if the passed name is
208 // not scoped, but a C# keyword, return the "@"-prefixed name;
209 // otherwise, check if the name is one of the method names of baseTypes;
210 // if so, prefix it with ice_; otherwise, return the name unchanged.
211 //
212 string
fixId(const string & name,int baseTypes,bool mangleCasts)213 Slice::CsGenerator::fixId(const string& name, int baseTypes, bool mangleCasts)
214 {
215     if(name.empty())
216     {
217         return name;
218     }
219     if(name[0] != ':')
220     {
221         return lookupKwd(name, baseTypes, mangleCasts);
222     }
223     StringList ids = splitScopedName(name);
224     StringList newIds;
225     for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i)
226     {
227         newIds.push_back(lookupKwd(*i, baseTypes));
228     }
229     stringstream result;
230     for(StringList::const_iterator j = newIds.begin(); j != newIds.end(); ++j)
231     {
232         if(j != newIds.begin())
233         {
234             result << '.';
235         }
236         result << *j;
237     }
238     return result.str();
239 }
240 
241 string
fixId(const ContainedPtr & cont,int baseTypes,bool mangleCasts)242 Slice::CsGenerator::fixId(const ContainedPtr& cont, int baseTypes, bool mangleCasts)
243 {
244     ContainerPtr container = cont->container();
245     ContainedPtr contained = ContainedPtr::dynamicCast(container);
246     if(contained && contained->hasMetaData("cs:property") &&
247        (contained->containedType() == Contained::ContainedTypeClass ||
248         contained->containedType() == Contained::ContainedTypeStruct))
249     {
250         return "_" + cont->name();
251     }
252     else
253     {
254         return fixId(cont->name(), baseTypes, mangleCasts);
255     }
256 }
257 
258 string
getOptionalFormat(const TypePtr & type,const string & scope)259 Slice::CsGenerator::getOptionalFormat(const TypePtr& type, const string& scope)
260 {
261     BuiltinPtr bp = BuiltinPtr::dynamicCast(type);
262     string prefix = getUnqualified("Ice.OptionalFormat", scope);
263     if(bp)
264     {
265         switch(bp->kind())
266         {
267         case Builtin::KindByte:
268         case Builtin::KindBool:
269         {
270             return prefix + ".F1";
271         }
272         case Builtin::KindShort:
273         {
274             return prefix + ".F2";
275         }
276         case Builtin::KindInt:
277         case Builtin::KindFloat:
278         {
279             return prefix + ".F4";
280         }
281         case Builtin::KindLong:
282         case Builtin::KindDouble:
283         {
284             return prefix + ".F8";
285         }
286         case Builtin::KindString:
287         {
288             return prefix + ".VSize";
289         }
290         case Builtin::KindObject:
291         {
292             return prefix + ".Class";
293         }
294         case Builtin::KindObjectProxy:
295         {
296             return prefix + ".FSize";
297         }
298         case Builtin::KindLocalObject:
299         {
300             assert(false);
301             break;
302         }
303         case Builtin::KindValue:
304         {
305             return prefix + ".Class";
306         }
307         }
308     }
309 
310     if(EnumPtr::dynamicCast(type))
311     {
312         return prefix + ".Size";
313     }
314 
315     SequencePtr seq = SequencePtr::dynamicCast(type);
316     if(seq)
317     {
318         if(seq->type()->isVariableLength())
319         {
320             return prefix + ".FSize";
321         }
322         else
323         {
324             return prefix + ".VSize";
325         }
326     }
327 
328     DictionaryPtr d = DictionaryPtr::dynamicCast(type);
329     if(d)
330     {
331         if(d->keyType()->isVariableLength() || d->valueType()->isVariableLength())
332         {
333             return prefix + ".FSize";
334         }
335         else
336         {
337             return prefix + ".VSize";
338         }
339     }
340 
341     StructPtr st = StructPtr::dynamicCast(type);
342     if(st)
343     {
344         if(st->isVariableLength())
345         {
346             return prefix + ".FSize";
347         }
348         else
349         {
350             return prefix + ".VSize";
351         }
352     }
353 
354     if(ProxyPtr::dynamicCast(type))
355     {
356         return prefix + ".FSize";
357     }
358 
359     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
360     assert(cl);
361     return prefix + ".Class";
362 }
363 
364 string
getStaticId(const TypePtr & type)365 Slice::CsGenerator::getStaticId(const TypePtr& type)
366 {
367     BuiltinPtr b = BuiltinPtr::dynamicCast(type);
368     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
369 
370     assert(isClassType(type));
371 
372     if(b)
373     {
374         return "Ice.Value.ice_staticId()";
375     }
376     else if(cl->isInterface())
377     {
378         ContainedPtr cont = ContainedPtr::dynamicCast(cl->container());
379         assert(cont);
380         return getUnqualified(cont) + "." + cl->name() + "Disp_.ice_staticId()";
381     }
382     else
383     {
384         return getUnqualified(cl) + ".ice_staticId()";
385     }
386 }
387 
388 string
typeToString(const TypePtr & type,const string & package,bool optional,bool local,const StringList & metaData)389 Slice::CsGenerator::typeToString(const TypePtr& type, const string& package, bool optional, bool local,
390                                  const StringList& metaData)
391 {
392     if(!type)
393     {
394         return "void";
395     }
396 
397     if(optional)
398     {
399         return getUnqualified("Ice.Optional", package) + "<" + typeToString(type, package, false, local) + ">";
400     }
401 
402     static const char* builtinTable[] =
403     {
404         "byte",
405         "bool",
406         "short",
407         "int",
408         "long",
409         "float",
410         "double",
411         "string",
412         "Ice.Object",
413         "Ice.ObjectPrx",
414         "System.Object",
415         "Ice.Value"
416     };
417 
418     if(local)
419     {
420         for(StringList::const_iterator i = metaData.begin(); i != metaData.end(); ++i)
421         {
422             const string clrType = "cs:type:";
423             const string meta = *i;
424             if(meta.find(clrType) == 0)
425             {
426                 return meta.substr(clrType.size());
427             }
428         }
429     }
430 
431     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
432     if(builtin)
433     {
434         if(!local && builtin->kind() == Builtin::KindObject)
435         {
436             return getUnqualified(builtinTable[Builtin::KindValue], package, true);
437         }
438         else
439         {
440             return getUnqualified(builtinTable[builtin->kind()], package, true);
441         }
442     }
443 
444     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
445     if(cl)
446     {
447         if(cl->isInterface() && !local)
448         {
449             return getUnqualified("Ice.Value", package);
450         }
451         else
452         {
453             return getUnqualified(cl, package);
454         }
455     }
456 
457     ProxyPtr proxy = ProxyPtr::dynamicCast(type);
458     if(proxy)
459     {
460         ClassDefPtr def = proxy->_class()->definition();
461         if(def->isInterface() || def->allOperations().size() > 0)
462         {
463             return getUnqualified(proxy->_class(), package, "", "Prx");
464         }
465         else
466         {
467             return getUnqualified("Ice.ObjectPrx", package);
468         }
469     }
470 
471     SequencePtr seq = SequencePtr::dynamicCast(type);
472     if(seq)
473     {
474         string prefix = "cs:generic:";
475         string meta;
476         if(seq->findMetaData(prefix, meta))
477         {
478             string customType = meta.substr(prefix.size());
479             if(customType == "List" || customType == "LinkedList" || customType == "Queue" || customType == "Stack")
480             {
481                 return "global::System.Collections.Generic." + customType + "<" +
482                     typeToString(seq->type(), package, optional, local) + ">";
483             }
484             else
485             {
486                 return "global::" + customType + "<" + typeToString(seq->type(), package, optional, local) + ">";
487             }
488         }
489 
490         prefix = "cs:serializable:";
491         if(seq->findMetaData(prefix, meta))
492         {
493             string customType = meta.substr(prefix.size());
494             return "global::" + customType;
495         }
496 
497         return typeToString(seq->type(), package, optional, local) + "[]";
498     }
499 
500     DictionaryPtr d = DictionaryPtr::dynamicCast(type);
501     if(d)
502     {
503         string prefix = "cs:generic:";
504         string meta;
505         string typeName;
506         if(d->findMetaData(prefix, meta))
507         {
508             typeName = meta.substr(prefix.size());
509         }
510         else
511         {
512             typeName = "Dictionary";
513         }
514         return "global::System.Collections.Generic." + typeName + "<" +
515             typeToString(d->keyType(), package, optional, local) + ", " +
516             typeToString(d->valueType(), package, optional, local) + ">";
517     }
518 
519     ContainedPtr contained = ContainedPtr::dynamicCast(type);
520     if(contained)
521     {
522         return getUnqualified(contained, package);
523     }
524 
525     return "???";
526 }
527 
528 string
resultStructName(const string & className,const string & opName,bool marshaledResult)529 Slice::CsGenerator::resultStructName(const string& className, const string& opName, bool marshaledResult)
530 {
531     ostringstream s;
532     s << className
533       << "_"
534       << IceUtilInternal::toUpper(opName.substr(0, 1))
535       << opName.substr(1)
536       << (marshaledResult ? "MarshaledResult" : "Result");
537     return s.str();
538 }
539 
540 string
resultType(const OperationPtr & op,const string & package,bool dispatch)541 Slice::CsGenerator::resultType(const OperationPtr& op, const string& package, bool dispatch)
542 {
543     ClassDefPtr cl = ClassDefPtr::dynamicCast(op->container()); // Get the class containing the op.
544     if(dispatch && op->hasMarshaledResult())
545     {
546         return getUnqualified(cl, package, "", resultStructName("", op->name(), true));
547     }
548 
549     string t;
550     ParamDeclList outParams = op->outParameters();
551     if(op->returnType() || !outParams.empty())
552     {
553         if(outParams.empty())
554         {
555             t = typeToString(op->returnType(), package, op->returnIsOptional(), cl->isLocal());
556         }
557         else if(op->returnType() || outParams.size() > 1)
558         {
559             t = getUnqualified(cl, package, "", resultStructName("", op->name()));
560         }
561         else
562         {
563             t = typeToString(outParams.front()->type(), package, outParams.front()->optional(), cl->isLocal());
564         }
565     }
566 
567     return t;
568 }
569 
570 string
taskResultType(const OperationPtr & op,const string & scope,bool dispatch)571 Slice::CsGenerator::taskResultType(const OperationPtr& op, const string& scope, bool dispatch)
572 {
573     string t = resultType(op, scope, dispatch);
574     if(t.empty())
575     {
576         return "global::System.Threading.Tasks.Task";
577     }
578     else
579     {
580         return "global::System.Threading.Tasks.Task<" + t + '>';
581     }
582 }
583 
584 bool
isClassType(const TypePtr & type)585 Slice::CsGenerator::isClassType(const TypePtr& type)
586 {
587     if(ClassDeclPtr::dynamicCast(type))
588     {
589         return true;
590     }
591     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
592     return builtin && (builtin->kind() == Builtin::KindObject || builtin->kind() == Builtin::KindValue);
593 }
594 
595 bool
isValueType(const TypePtr & type)596 Slice::CsGenerator::isValueType(const TypePtr& type)
597 {
598     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
599     if(builtin)
600     {
601         switch(builtin->kind())
602         {
603             case Builtin::KindString:
604             case Builtin::KindObject:
605             case Builtin::KindObjectProxy:
606             case Builtin::KindLocalObject:
607             case Builtin::KindValue:
608             {
609                 return false;
610                 break;
611             }
612             default:
613             {
614                 return true;
615                 break;
616             }
617         }
618     }
619     StructPtr s = StructPtr::dynamicCast(type);
620     if(s)
621     {
622         if(s->hasMetaData("cs:class"))
623         {
624             return false;
625         }
626         DataMemberList dm = s->dataMembers();
627         for(DataMemberList::const_iterator i = dm.begin(); i != dm.end(); ++i)
628         {
629             if(!isValueType((*i)->type()) || (*i)->defaultValueType())
630             {
631                 return false;
632             }
633         }
634         return true;
635     }
636     if(EnumPtr::dynamicCast(type))
637     {
638         return true;
639     }
640     return false;
641 }
642 
643 void
writeMarshalUnmarshalCode(Output & out,const TypePtr & type,const string & package,const string & param,bool marshal,const string & customStream)644 Slice::CsGenerator::writeMarshalUnmarshalCode(Output &out,
645                                               const TypePtr& type,
646                                               const string& package,
647                                               const string& param,
648                                               bool marshal,
649                                               const string& customStream)
650 {
651     string stream = customStream;
652     if(stream.empty())
653     {
654         stream = marshal ? "ostr" : "istr";
655     }
656 
657     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
658     if(builtin)
659     {
660         switch(builtin->kind())
661         {
662             case Builtin::KindByte:
663             {
664                 if(marshal)
665                 {
666                     out << nl << stream << ".writeByte(" << param << ");";
667                 }
668                 else
669                 {
670                     out << nl << param << " = " << stream << ".readByte()" << ';';
671                 }
672                 break;
673             }
674             case Builtin::KindBool:
675             {
676                 if(marshal)
677                 {
678                     out << nl << stream << ".writeBool(" << param << ");";
679                 }
680                 else
681                 {
682                     out << nl << param << " = " << stream << ".readBool()" << ';';
683                 }
684                 break;
685             }
686             case Builtin::KindShort:
687             {
688                 if(marshal)
689                 {
690                     out << nl << stream << ".writeShort(" << param << ");";
691                 }
692                 else
693                 {
694                     out << nl << param << " = " << stream << ".readShort()" << ';';
695                 }
696                 break;
697             }
698             case Builtin::KindInt:
699             {
700                 if(marshal)
701                 {
702                     out << nl << stream << ".writeInt(" << param << ");";
703                 }
704                 else
705                 {
706                     out << nl << param << " = " << stream << ".readInt()" << ';';
707                 }
708                 break;
709             }
710             case Builtin::KindLong:
711             {
712                 if(marshal)
713                 {
714                     out << nl << stream << ".writeLong(" << param << ");";
715                 }
716                 else
717                 {
718                     out << nl << param << " = " << stream << ".readLong()" << ';';
719                 }
720                 break;
721             }
722             case Builtin::KindFloat:
723             {
724                 if(marshal)
725                 {
726                     out << nl << stream << ".writeFloat(" << param << ");";
727                 }
728                 else
729                 {
730                     out << nl << param << " = " << stream << ".readFloat()" << ';';
731                 }
732                 break;
733             }
734             case Builtin::KindDouble:
735             {
736                 if(marshal)
737                 {
738                     out << nl << stream << ".writeDouble(" << param << ");";
739                 }
740                 else
741                 {
742                     out << nl << param << " = " << stream << ".readDouble()" << ';';
743                 }
744                 break;
745             }
746             case Builtin::KindString:
747             {
748                 if(marshal)
749                 {
750                     out << nl << stream << ".writeString(" << param << ");";
751                 }
752                 else
753                 {
754                     out << nl << param << " = " << stream << ".readString()" << ';';
755                 }
756                 break;
757             }
758             case Builtin::KindObject:
759             case Builtin::KindValue:
760             {
761                 if(marshal)
762                 {
763                     out << nl << stream << ".writeValue(" << param << ");";
764                 }
765                 else
766                 {
767                     out << nl << stream << ".readValue(" << param << ");";
768                 }
769                 break;
770             }
771             case Builtin::KindObjectProxy:
772             {
773                 string typeS = typeToString(type, package);
774                 if(marshal)
775                 {
776                     out << nl << stream << ".writeProxy(" << param << ");";
777                 }
778                 else
779                 {
780                     out << nl << param << " = " << stream << ".readProxy()" << ';';
781                 }
782                 break;
783             }
784             case Builtin::KindLocalObject:
785             {
786                 assert(false);
787                 break;
788             }
789         }
790         return;
791     }
792 
793     ProxyPtr prx = ProxyPtr::dynamicCast(type);
794     if(prx)
795     {
796         ClassDefPtr def = prx->_class()->definition();
797         if(def->isInterface() || def->allOperations().size() > 0)
798         {
799             string typeS = typeToString(type, package);
800             if(marshal)
801             {
802                 out << nl << typeS << "Helper.write(" << stream << ", " << param << ");";
803             }
804             else
805             {
806                 out << nl << param << " = " << typeS << "Helper.read(" << stream << ");";
807             }
808         }
809         else
810         {
811             if(marshal)
812             {
813                 out << nl << stream << ".writeProxy(" << param << ");";
814             }
815             else
816             {
817                 out << nl << param << " = " << stream << ".readProxy()" << ';';
818             }
819         }
820         return;
821     }
822 
823     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
824     if(cl)
825     {
826         if(marshal)
827         {
828             out << nl << stream << ".writeValue(" << param << ");";
829         }
830         else
831         {
832             out << nl << stream << ".readValue(" << param << ");";
833         }
834         return;
835     }
836 
837     StructPtr st = StructPtr::dynamicCast(type);
838     if(st)
839     {
840         if(marshal)
841         {
842             if(!isValueType(st))
843             {
844                 out << nl << typeToString(st, package) << ".ice_write(" << stream << ", " << param << ");";
845             }
846             else
847             {
848                 out << nl << param << ".ice_writeMembers(" << stream << ");";
849             }
850         }
851         else
852         {
853             if(!isValueType(st))
854             {
855                 out << nl << param << " = " << typeToString(type, package) << ".ice_read(" << stream << ");";
856             }
857             else
858             {
859                 out << nl << param << ".ice_readMembers(" << stream << ");";
860             }
861         }
862         return;
863     }
864 
865     EnumPtr en = EnumPtr::dynamicCast(type);
866     if(en)
867     {
868         if(marshal)
869         {
870             out << nl << stream << ".writeEnum((int)" << param << ", " << en->maxValue() << ");";
871         }
872         else
873         {
874             out << nl << param << " = (" << typeToString(type, package) << ')' << stream << ".readEnum(" << en->maxValue()
875                 << ");";
876         }
877         return;
878     }
879 
880     SequencePtr seq = SequencePtr::dynamicCast(type);
881     if(seq)
882     {
883         writeSequenceMarshalUnmarshalCode(out, seq, package, param, marshal, true, stream);
884         return;
885     }
886 
887     assert(ConstructedPtr::dynamicCast(type));
888     string helperName;
889     DictionaryPtr d = DictionaryPtr::dynamicCast(type);
890     if(d)
891     {
892         helperName = getUnqualified(d, package, "", "Helper");
893     }
894     else
895     {
896         helperName = typeToString(type, package) + "Helper";
897     }
898 
899     if(marshal)
900     {
901         out << nl << helperName << ".write(" << stream << ", " << param << ");";
902     }
903     else
904     {
905         out << nl << param << " = " << helperName << ".read(" << stream << ')' << ';';
906     }
907 }
908 
909 void
writeOptionalMarshalUnmarshalCode(Output & out,const TypePtr & type,const string & scope,const string & param,int tag,bool marshal,const string & customStream)910 Slice::CsGenerator::writeOptionalMarshalUnmarshalCode(Output &out,
911                                                       const TypePtr& type,
912                                                       const string& scope,
913                                                       const string& param,
914                                                       int tag,
915                                                       bool marshal,
916                                                       const string& customStream)
917 {
918     string stream = customStream;
919     if(stream.empty())
920     {
921         stream = marshal ? "ostr" : "istr";
922     }
923 
924     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
925     if(builtin)
926     {
927         switch(builtin->kind())
928         {
929             case Builtin::KindByte:
930             {
931                 if(marshal)
932                 {
933                     out << nl << stream << ".writeByte(" << tag << ", " << param << ");";
934                 }
935                 else
936                 {
937                     //
938                     // BUGFIX: with .NET Core reading the byte optional directly in the
939                     // result struct can fails unexpectly with optimized builds.
940                     //
941                     if(param.find(".") != string::npos)
942                     {
943                         out << sb;
944                         out << nl << "var tmp = " << stream << ".readByte(" << tag << ");";
945                         out << nl << param << " = tmp;";
946                         out << eb;
947                     }
948                     else
949                     {
950                         out << nl << param << " = " << stream << ".readByte(" << tag << ");";
951                     }
952                 }
953                 break;
954             }
955             case Builtin::KindBool:
956             {
957                 if(marshal)
958                 {
959                     out << nl << stream << ".writeBool(" << tag << ", " << param << ");";
960                 }
961                 else
962                 {
963                     //
964                     // BUGFIX: with .NET Core reading the bool optional directly in the
965                     // result struct fails unexpectly with optimized builds.
966                     //
967                     if(param.find(".") != string::npos)
968                     {
969                         out << sb;
970                         out << nl << "var tmp = " << stream << ".readBool(" << tag << ");";
971                         out << nl << param << " = tmp;";
972                         out << eb;
973                     }
974                     else
975                     {
976                         out << nl << param << " = " << stream << ".readBool(" << tag << ");";
977                     }
978                 }
979                 break;
980             }
981             case Builtin::KindShort:
982             {
983                 if(marshal)
984                 {
985                     out << nl << stream << ".writeShort(" << tag << ", " << param << ");";
986                 }
987                 else
988                 {
989                     out << nl << param << " = " << stream << ".readShort(" << tag << ");";
990                 }
991                 break;
992             }
993             case Builtin::KindInt:
994             {
995                 if(marshal)
996                 {
997                     out << nl << stream << ".writeInt(" << tag << ", " << param << ");";
998                 }
999                 else
1000                 {
1001                     out << nl << param << " = " << stream << ".readInt(" << tag << ");";
1002                 }
1003                 break;
1004             }
1005             case Builtin::KindLong:
1006             {
1007                 if(marshal)
1008                 {
1009                     out << nl << stream << ".writeLong(" << tag << ", " << param << ");";
1010                 }
1011                 else
1012                 {
1013                     out << nl << param << " = " << stream << ".readLong(" << tag << ");";
1014                 }
1015                 break;
1016             }
1017             case Builtin::KindFloat:
1018             {
1019                 if(marshal)
1020                 {
1021                     out << nl << stream << ".writeFloat(" << tag << ", " << param << ");";
1022                 }
1023                 else
1024                 {
1025                     out << nl << param << " = " << stream << ".readFloat(" << tag << ");";
1026                 }
1027                 break;
1028             }
1029             case Builtin::KindDouble:
1030             {
1031                 if(marshal)
1032                 {
1033                     out << nl << stream << ".writeDouble(" << tag << ", " << param << ");";
1034                 }
1035                 else
1036                 {
1037                     out << nl << param << " = " << stream << ".readDouble(" << tag << ");";
1038                 }
1039                 break;
1040             }
1041             case Builtin::KindString:
1042             {
1043                 if(marshal)
1044                 {
1045                     out << nl << stream << ".writeString(" << tag << ", " << param << ");";
1046                 }
1047                 else
1048                 {
1049                     out << nl << param << " = " << stream << ".readString(" << tag << ");";
1050                 }
1051                 break;
1052             }
1053             case Builtin::KindObject:
1054             case Builtin::KindValue:
1055             {
1056                 if(marshal)
1057                 {
1058                     out << nl << stream << ".writeValue(" << tag << ", " << param << ");";
1059                 }
1060                 else
1061                 {
1062                     out << nl << stream << ".readValue(" << tag << ", " << param << ");";
1063                 }
1064                 break;
1065             }
1066             case Builtin::KindObjectProxy:
1067             {
1068                 string typeS = typeToString(type, scope);
1069                 if(marshal)
1070                 {
1071                     out << nl << stream << ".writeProxy(" << tag << ", " << param << ");";
1072                 }
1073                 else
1074                 {
1075                     out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<"
1076                         << getUnqualified("Ice.ObjectPrx", scope) << ">(" << stream << ".readProxy(" << tag << "));";
1077                 }
1078                 break;
1079             }
1080             case Builtin::KindLocalObject:
1081             {
1082                 assert(false);
1083                 break;
1084             }
1085         }
1086         return;
1087     }
1088 
1089     ProxyPtr prx = ProxyPtr::dynamicCast(type);
1090     if(prx)
1091     {
1092         if(marshal)
1093         {
1094             out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag
1095                 << ", " << getUnqualified("Ice.OptionalFormat", scope) << ".FSize))";
1096             out << sb;
1097             out << nl << "int pos = " << stream << ".startSize();";
1098             writeMarshalUnmarshalCode(out, type, scope, param + ".Value", marshal, customStream);
1099             out << nl << stream << ".endSize(pos);";
1100             out << eb;
1101         }
1102         else
1103         {
1104             out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getUnqualified("Ice.OptionalFormat", scope)
1105                 << ".FSize))";
1106             out << sb;
1107             out << nl << stream << ".skip(4);";
1108             string tmp = "tmpVal";
1109             string typeS = typeToString(type, scope);
1110             out << nl << typeS << ' ' << tmp << ';';
1111             writeMarshalUnmarshalCode(out, type, scope, tmp, marshal, customStream);
1112             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">(" << tmp << ");";
1113             out << eb;
1114             out << nl << "else";
1115             out << sb;
1116             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">();";
1117             out << eb;
1118         }
1119         return;
1120     }
1121 
1122     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
1123     if(cl)
1124     {
1125         if(marshal)
1126         {
1127             out << nl << stream << ".writeValue(" << tag << ", " << param << ");";
1128         }
1129         else
1130         {
1131             out << nl << stream << ".readValue(" << tag << ", " << param << ");";
1132         }
1133         return;
1134     }
1135 
1136     StructPtr st = StructPtr::dynamicCast(type);
1137     if(st)
1138     {
1139         if(marshal)
1140         {
1141             out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag << ", "
1142                 << getOptionalFormat(st, scope) << "))";
1143             out << sb;
1144             if(st->isVariableLength())
1145             {
1146                 out << nl << "int pos = " << stream << ".startSize();";
1147             }
1148             else
1149             {
1150                 out << nl << stream << ".writeSize(" << st->minWireSize() << ");";
1151             }
1152             writeMarshalUnmarshalCode(out, type, scope, param + ".Value", marshal, customStream);
1153             if(st->isVariableLength())
1154             {
1155                 out << nl << stream << ".endSize(pos);";
1156             }
1157             out << eb;
1158         }
1159         else
1160         {
1161             out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(st, scope) << "))";
1162             out << sb;
1163             if(st->isVariableLength())
1164             {
1165                 out << nl << stream << ".skip(4);";
1166             }
1167             else
1168             {
1169                 out << nl << stream << ".skipSize();";
1170             }
1171             string typeS = typeToString(type, scope);
1172             string tmp = "tmpVal";
1173             if(isValueType(st))
1174             {
1175                 out << nl << typeS << ' ' << tmp << " = new " << typeS << "();";
1176             }
1177             else
1178             {
1179                 out << nl << typeS << ' ' << tmp << " = null;";
1180             }
1181             writeMarshalUnmarshalCode(out, type, scope, tmp, marshal, customStream);
1182             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">(" << tmp << ");";
1183             out << eb;
1184             out << nl << "else";
1185             out << sb;
1186             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">();";
1187             out << eb;
1188         }
1189         return;
1190     }
1191 
1192     EnumPtr en = EnumPtr::dynamicCast(type);
1193     if(en)
1194     {
1195         size_t sz = en->enumerators().size();
1196         if(marshal)
1197         {
1198             out << nl << "if(" << param << ".HasValue)";
1199             out << sb;
1200             out << nl << stream << ".writeEnum(" << tag << ", (int)" << param << ".Value, " << sz << ");";
1201             out << eb;
1202         }
1203         else
1204         {
1205             out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getUnqualified("Ice.OptionalFormat", scope)
1206                 << ".Size))";
1207             out << sb;
1208             string typeS = typeToString(type, scope);
1209             string tmp = "tmpVal";
1210             out << nl << typeS << ' ' << tmp << ';';
1211             writeMarshalUnmarshalCode(out, type, scope, tmp, marshal, customStream);
1212             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">(" << tmp << ");";
1213             out << eb;
1214             out << nl << "else";
1215             out << sb;
1216             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">();";
1217             out << eb;
1218         }
1219         return;
1220     }
1221 
1222     SequencePtr seq = SequencePtr::dynamicCast(type);
1223     if(seq)
1224     {
1225         writeOptionalSequenceMarshalUnmarshalCode(out, seq, scope, param, tag, marshal, stream);
1226         return;
1227     }
1228 
1229     DictionaryPtr d = DictionaryPtr::dynamicCast(type);
1230     assert(d);
1231     TypePtr keyType = d->keyType();
1232     TypePtr valueType = d->valueType();
1233     if(marshal)
1234     {
1235         out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag << ", "
1236             << getOptionalFormat(d, scope) << "))";
1237         out << sb;
1238         if(keyType->isVariableLength() || valueType->isVariableLength())
1239         {
1240             out << nl << "int pos = " << stream << ".startSize();";
1241         }
1242         else
1243         {
1244             out << nl << stream << ".writeSize(" << param << ".Value == null ? 1 : " << param << ".Value.Count * "
1245                 << (keyType->minWireSize() + valueType->minWireSize()) << " + (" << param
1246                 << ".Value.Count > 254 ? 5 : 1));";
1247         }
1248         writeMarshalUnmarshalCode(out, type, scope, param + ".Value", marshal, customStream);
1249         if(keyType->isVariableLength() || valueType->isVariableLength())
1250         {
1251             out << nl << stream << ".endSize(pos);";
1252         }
1253         out << eb;
1254     }
1255     else
1256     {
1257         out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(d, scope) << "))";
1258         out << sb;
1259         if(keyType->isVariableLength() || valueType->isVariableLength())
1260         {
1261             out << nl << stream << ".skip(4);";
1262         }
1263         else
1264         {
1265             out << nl << stream << ".skipSize();";
1266         }
1267         string typeS = typeToString(type, scope);
1268         string tmp = "tmpVal";
1269         out << nl << typeS << ' ' << tmp << " = new " << typeS << "();";
1270         writeMarshalUnmarshalCode(out, type, scope, tmp, marshal, customStream);
1271         out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">(" << tmp << ");";
1272         out << eb;
1273         out << nl << "else";
1274         out << sb;
1275         out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << typeS << ">();";
1276         out << eb;
1277     }
1278 }
1279 
1280 void
writeSequenceMarshalUnmarshalCode(Output & out,const SequencePtr & seq,const string & scope,const string & param,bool marshal,bool useHelper,const string & customStream)1281 Slice::CsGenerator::writeSequenceMarshalUnmarshalCode(Output& out,
1282                                                       const SequencePtr& seq,
1283                                                       const string& scope,
1284                                                       const string& param,
1285                                                       bool marshal,
1286                                                       bool useHelper,
1287                                                       const string& customStream)
1288 {
1289     string stream = customStream;
1290     if(stream.empty())
1291     {
1292         stream = marshal ? "ostr" : "istr";
1293     }
1294 
1295     ContainedPtr cont = ContainedPtr::dynamicCast(seq->container());
1296     assert(cont);
1297     if(useHelper)
1298     {
1299         string helperName = getUnqualified(getNamespace(seq) + "." + seq->name() + "Helper", scope);
1300         if(marshal)
1301         {
1302             out << nl << helperName << ".write(" << stream << ", " << param << ");";
1303         }
1304         else
1305         {
1306             out << nl << param << " = " << helperName << ".read(" << stream << ");";
1307         }
1308         return;
1309     }
1310 
1311     TypePtr type = seq->type();
1312     string typeS = typeToString(type, scope);
1313 
1314     const string genericPrefix = "cs:generic:";
1315     string genericType;
1316     string addMethod = "Add";
1317     const bool isGeneric = seq->findMetaData(genericPrefix, genericType);
1318     bool isStack = false;
1319     bool isList = false;
1320     bool isLinkedList = false;
1321     bool isCustom = false;
1322     if(isGeneric)
1323     {
1324         genericType = genericType.substr(genericPrefix.size());
1325         if(genericType == "LinkedList")
1326         {
1327             addMethod = "AddLast";
1328             isLinkedList = true;
1329         }
1330         else if(genericType == "Queue")
1331         {
1332             addMethod = "Enqueue";
1333         }
1334         else if(genericType == "Stack")
1335         {
1336             addMethod = "Push";
1337             isStack = true;
1338         }
1339         else if(genericType == "List")
1340         {
1341             isList = true;
1342         }
1343         else
1344         {
1345             isCustom = true;
1346         }
1347     }
1348 
1349     const bool isArray = !isGeneric;
1350     const string limitID = isArray ? "Length" : "Count";
1351 
1352     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
1353     ProxyPtr proxy = ProxyPtr::dynamicCast(type);
1354     ClassDefPtr clsDef;
1355     if(proxy)
1356     {
1357         clsDef = proxy->_class()->definition();
1358     }
1359     bool isObjectProxySeq = clsDef && !clsDef->isInterface() && clsDef->allOperations().size() == 0;
1360     Builtin::Kind kind = builtin ? builtin->kind() : Builtin::KindObjectProxy;
1361 
1362     if(builtin || isObjectProxySeq)
1363     {
1364         switch(kind)
1365         {
1366             case Builtin::KindValue:
1367             case Builtin::KindObject:
1368             case Builtin::KindObjectProxy:
1369             {
1370                 if(marshal)
1371                 {
1372                     out << nl << "if(" << param << " == null)";
1373                     out << sb;
1374                     out << nl << stream << ".writeSize(0);";
1375                     out << eb;
1376                     out << nl << "else";
1377                     out << sb;
1378                     out << nl << stream << ".writeSize(" << param << '.' << limitID << ");";
1379                     if(isGeneric && !isList)
1380                     {
1381                         if(isStack)
1382                         {
1383                             //
1384                             // If the collection is a stack, write in top-to-bottom order. Stacks
1385                             // cannot contain Ice.Object.
1386                             //
1387                             out << nl << getUnqualified("Ice.ObjectPrx", scope) << "[] " << param << "_tmp = " << param
1388                                 << ".ToArray();";
1389                             out << nl << "for(int ix = 0; ix < " << param << "_tmp.Length; ++ix)";
1390                             out << sb;
1391                             out << nl << stream << ".writeProxy(" << param << "_tmp[ix]);";
1392                             out << eb;
1393                         }
1394                         else
1395                         {
1396                             out << nl << "global::System.Collections.Generic.IEnumerator<" << typeS
1397                                 << "> e = " << param << ".GetEnumerator();";
1398                             out << nl << "while(e.MoveNext())";
1399                             out << sb;
1400                             string func = (kind == Builtin::KindObject ||
1401                                            kind == Builtin::KindValue) ? "writeValue" : "writeProxy";
1402                             out << nl << stream << '.' << func << "(e.Current);";
1403                             out << eb;
1404                         }
1405                     }
1406                     else
1407                     {
1408                         out << nl << "for(int ix = 0; ix < " << param << '.' << limitID << "; ++ix)";
1409                         out << sb;
1410                         string func = (kind == Builtin::KindObject ||
1411                                        kind == Builtin::KindValue) ? "writeValue" : "writeProxy";
1412                         out << nl << stream << '.' << func << '(' << param << "[ix]);";
1413                         out << eb;
1414                     }
1415                     out << eb;
1416                 }
1417                 else
1418                 {
1419                     out << nl << "int " << param << "_lenx = " << stream << ".readAndCheckSeqSize("
1420                         << static_cast<unsigned>(type->minWireSize()) << ");";
1421                     if(!isStack)
1422                     {
1423                         out << nl << param << " = new ";
1424                     }
1425                     if((kind == Builtin::KindObject || kind == Builtin::KindValue))
1426                     {
1427                         string patcherName;
1428                         if(isArray)
1429                         {
1430                             patcherName = "global::IceInternal.Patcher.arrayReadValue";
1431                             out << getUnqualified("Ice.Value", scope) << "[" << param << "_lenx];";
1432                         }
1433                         else if(isCustom)
1434                         {
1435                             patcherName = "global::IceInternal.Patcher.customSeqReadValue";
1436                             out << "global::" << genericType << "<" << getUnqualified("Ice.Value", scope) << ">();";
1437                         }
1438                         else
1439                         {
1440                             patcherName = "global::IceInternal.Patcher.listReadValue";
1441                             out << "global::System.Collections.Generic." << genericType << "<"
1442                                 << getUnqualified("Ice.Value", scope) << ">(" << param << "_lenx);";
1443                         }
1444                         out << nl << "for(int ix = 0; ix < " << param << "_lenx; ++ix)";
1445                         out << sb;
1446                         out << nl << stream << ".readValue(" << patcherName << "<"
1447                             << getUnqualified("Ice.Value", scope) << ">(" << param << ", ix));";
1448                     }
1449                     else
1450                     {
1451                         if(isStack)
1452                         {
1453                             out << nl << getUnqualified("Ice.ObjectPrx", scope) << "[] " << param << "_tmp = new "
1454                                 << getUnqualified("Ice.ObjectPrx", scope) << "[" << param << "_lenx];";
1455                         }
1456                         else if(isArray)
1457                         {
1458                             out << getUnqualified("Ice.ObjectPrx", scope) << "[" << param << "_lenx];";
1459                         }
1460                         else if(isCustom)
1461                         {
1462                             out << "global::" << genericType << "<" << getUnqualified("Ice.ObjectPrx", scope)
1463                                 << ">();";
1464                         }
1465                         else
1466                         {
1467                             out << "global::System.Collections.Generic." << genericType << "<"
1468                                 << getUnqualified("Ice.ObjectPrx", scope) << ">(";
1469                             if(!isLinkedList)
1470                             {
1471                                 out << param << "_lenx";
1472                             }
1473                             out << ");";
1474                         }
1475 
1476                         out << nl << "for(int ix = 0; ix < " << param << "_lenx; ++ix)";
1477                         out << sb;
1478                         if(isArray || isStack)
1479                         {
1480                             string v = isArray ? param : param + "_tmp";
1481                             out << nl << v << "[ix] = " << stream << ".readProxy();";
1482                         }
1483                         else
1484                         {
1485                             out << nl << getUnqualified("Ice.ObjectPrx", scope) << " val = new "
1486                                 << getUnqualified("Ice.ObjectPrxHelperBase", scope) << "();";
1487                             out << nl << "val = " << stream << ".readProxy();";
1488                             out << nl << param << "." << addMethod << "(val);";
1489                         }
1490                     }
1491                     out << eb;
1492 
1493                     if(isStack)
1494                     {
1495                         out << nl << "global::System.Array.Reverse(" << param << "_tmp);";
1496                         out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">("
1497                             << param << "_tmp);";
1498                     }
1499                 }
1500                 break;
1501             }
1502             default:
1503             {
1504                 string prefix = "cs:serializable:";
1505                 string meta;
1506                 if(seq->findMetaData(prefix, meta))
1507                 {
1508                     if(marshal)
1509                     {
1510                         out << nl << stream << ".writeSerializable(" << param << ");";
1511                     }
1512                     else
1513                     {
1514                         out << nl << param << " = (" << typeToString(seq, scope) << ")" << stream << ".readSerializable();";
1515                     }
1516                     break;
1517                 }
1518 
1519                 string func = typeS;
1520                 func[0] = static_cast<char>(toupper(static_cast<unsigned char>(typeS[0])));
1521                 if(marshal)
1522                 {
1523                     if(isArray)
1524                     {
1525                         out << nl << stream << ".write" << func << "Seq(" << param << ");";
1526                     }
1527                     else if(isCustom)
1528                     {
1529                         out << nl << stream << ".write" << func << "Seq(" << param << " == null ? 0 : "
1530                             << param << ".Count, " << param << ");";
1531                     }
1532                     else
1533                     {
1534                         assert(isGeneric);
1535                         out << nl << stream << ".write" << func << "Seq(" << param << " == null ? 0 : "
1536                             << param << ".Count, " << param << ");";
1537                     }
1538                 }
1539                 else
1540                 {
1541                     if(isArray)
1542                     {
1543                         out << nl << param << " = " << stream << ".read" << func << "Seq();";
1544                     }
1545                     else if(isCustom)
1546                     {
1547                         out << sb;
1548                         out << nl << param << " = new " << "global::" << genericType << "<"
1549                             << typeToString(type, scope) << ">();";
1550                         out << nl << "int szx = " << stream << ".readSize();";
1551                         out << nl << "for(int ix = 0; ix < szx; ++ix)";
1552                         out << sb;
1553                         out << nl << param << ".Add(" << stream << ".read" << func << "());";
1554                         out << eb;
1555                         out << eb;
1556                     }
1557                     else
1558                     {
1559                         assert(isGeneric);
1560                         out << nl << stream << ".read" << func << "Seq(out " << param << ");";
1561                     }
1562                 }
1563                 break;
1564             }
1565         }
1566         return;
1567     }
1568 
1569     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
1570     if(cl)
1571     {
1572         if(marshal)
1573         {
1574             out << nl << "if(" << param << " == null)";
1575             out << sb;
1576             out << nl << stream << ".writeSize(0);";
1577             out << eb;
1578             out << nl << "else";
1579             out << sb;
1580             out << nl << stream << ".writeSize(" << param << '.' << limitID << ");";
1581             if(isGeneric && !isList)
1582             {
1583                 //
1584                 // Stacks cannot contain class instances, so there is no need to marshal a
1585                 // stack bottom-up here.
1586                 //
1587                 out << nl << "global::System.Collections.Generic.IEnumerator<" << typeS
1588                     << "> e = " << param << ".GetEnumerator();";
1589                 out << nl << "while(e.MoveNext())";
1590                 out << sb;
1591                 out << nl << stream << ".writeValue(e.Current);";
1592                 out << eb;
1593             }
1594             else
1595             {
1596                 out << nl << "for(int ix = 0; ix < " << param << '.' << limitID << "; ++ix)";
1597                 out << sb;
1598                 out << nl << stream << ".writeValue(" << param << "[ix]);";
1599                 out << eb;
1600             }
1601             out << eb;
1602         }
1603         else
1604         {
1605             out << sb;
1606             out << nl << "int szx = " << stream << ".readAndCheckSeqSize("
1607                 << static_cast<unsigned>(type->minWireSize()) << ");";
1608             out << nl << param << " = new ";
1609             string patcherName;
1610             if(isArray)
1611             {
1612                 patcherName = "global::IceInternal.Patcher.arrayReadValue";
1613                 out << toArrayAlloc(typeS + "[]", "szx") << ";";
1614             }
1615             else if(isCustom)
1616             {
1617                 patcherName = "global::IceInternal.Patcher.customSeqReadValue";
1618                 out << "global::" << genericType << "<" << typeS << ">();";
1619             }
1620             else
1621             {
1622                 patcherName = "global::IceInternal.Patcher.listReadValue";
1623                 out << "global::System.Collections.Generic." << genericType << "<" << typeS << ">(szx);";
1624             }
1625             out << nl << "for(int ix = 0; ix < szx; ++ix)";
1626             out << sb;
1627             string scoped = ContainedPtr::dynamicCast(type)->scoped();
1628             out << nl << stream << ".readValue(" << patcherName << '<' << typeS << ">(" << param << ", ix));";
1629             out << eb;
1630             out << eb;
1631         }
1632         return;
1633     }
1634 
1635     StructPtr st = StructPtr::dynamicCast(type);
1636     if(st)
1637     {
1638         if(marshal)
1639         {
1640             out << nl << "if(" << param << " == null)";
1641             out << sb;
1642             out << nl << stream << ".writeSize(0);";
1643             out << eb;
1644             out << nl << "else";
1645             out << sb;
1646             out << nl << stream << ".writeSize(" << param << '.' << limitID << ");";
1647             if(isGeneric && !isList)
1648             {
1649                 //
1650                 // Stacks are marshaled top-down.
1651                 //
1652                 if(isStack)
1653                 {
1654                     out << nl << typeS << "[] " << param << "_tmp = " << param << ".ToArray();";
1655                     out << nl << "for(int ix = 0; ix < " << param << "_tmp.Length; ++ix)";
1656                 }
1657                 else
1658                 {
1659                     out << nl << "global::System.Collections.Generic.IEnumerator<" << typeS
1660                         << "> e = " << param << ".GetEnumerator();";
1661                     out << nl << "while(e.MoveNext())";
1662                 }
1663             }
1664             else
1665             {
1666                 out << nl << "for(int ix = 0; ix < " << param << '.' << limitID << "; ++ix)";
1667             }
1668             out << sb;
1669             string call;
1670             if(isGeneric && !isList && !isStack)
1671             {
1672                 if(isValueType(type))
1673                 {
1674                     call = "e.Current";
1675                 }
1676                 else
1677                 {
1678                     call = "(e.Current == null ? new ";
1679                     call += typeS + "() : e.Current)";
1680                 }
1681             }
1682             else
1683             {
1684                 if(isValueType(type))
1685                 {
1686                     call = param;
1687                     if(isStack)
1688                     {
1689                         call += "_tmp";
1690                     }
1691                 }
1692                 else
1693                 {
1694                     call = "(";
1695                     call += param;
1696                     if(isStack)
1697                     {
1698                         call += "_tmp";
1699                     }
1700                     call += "[ix] == null ? new " + typeS + "() : " + param;
1701                     if(isStack)
1702                     {
1703                         call += "_tmp";
1704                     }
1705                 }
1706                 call += "[ix]";
1707                 if(!isValueType(type))
1708                 {
1709                     call += ")";
1710                 }
1711             }
1712             call += ".";
1713             call += "ice_writeMembers";
1714             call += "(" + stream + ");";
1715             out << nl << call;
1716             out << eb;
1717             out << eb;
1718         }
1719         else
1720         {
1721             out << sb;
1722             out << nl << "int szx = " << stream << ".readAndCheckSeqSize("
1723                 << static_cast<unsigned>(type->minWireSize()) << ");";
1724             if(isArray)
1725             {
1726                 out << nl << param << " = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1727             }
1728             else if(isCustom)
1729             {
1730                 out << nl << param << " = new global::" << genericType << "<" << typeS << ">();";
1731             }
1732             else if(isStack)
1733             {
1734                 out << nl << typeS << "[] " << param << "_tmp = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1735             }
1736             else
1737             {
1738                 out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">(";
1739                 if(!isLinkedList)
1740                 {
1741                     out << "szx";
1742                 }
1743                 out << ");";
1744             }
1745             out << nl << "for(int ix = 0; ix < szx; ++ix)";
1746             out << sb;
1747             if(isArray || isStack)
1748             {
1749                 string v = isArray ? param : param + "_tmp";
1750                 if(!isValueType(st))
1751                 {
1752                     out << nl << v << "[ix] = new " << typeS << "();";
1753                 }
1754                 out << nl << v << "[ix].ice_readMembers(" << stream << ");";
1755             }
1756             else
1757             {
1758                 out << nl << typeS << " val = new " << typeS << "();";
1759                 out << nl << "val.ice_readMembers(" << stream << ");";
1760                 out << nl << param << "." << addMethod << "(val);";
1761             }
1762             out << eb;
1763             if(isStack)
1764             {
1765                 out << nl << "global::System.Array.Reverse(" << param << "_tmp);";
1766                 out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">("
1767                     << param << "_tmp);";
1768             }
1769             out << eb;
1770         }
1771         return;
1772     }
1773 
1774     EnumPtr en = EnumPtr::dynamicCast(type);
1775     if(en)
1776     {
1777         if(marshal)
1778         {
1779             out << nl << "if(" << param << " == null)";
1780             out << sb;
1781             out << nl << stream << ".writeSize(0);";
1782             out << eb;
1783             out << nl << "else";
1784             out << sb;
1785             out << nl << stream << ".writeSize(" << param << '.'<< limitID << ");";
1786             if(isGeneric && !isList)
1787             {
1788                 //
1789                 // Stacks are marshaled top-down.
1790                 //
1791                 if(isStack)
1792                 {
1793                     out << nl << typeS << "[] " << param << "_tmp = " << param << ".ToArray();";
1794                     out << nl << "for(int ix = 0; ix < " << param << "_tmp.Length; ++ix)";
1795                     out << sb;
1796                     out << nl << stream << ".writeEnum((int)" << param << "_tmp[ix], " << en->maxValue() << ");";
1797                     out << eb;
1798                 }
1799                 else
1800                 {
1801                     out << nl << "global::System.Collections.Generic.IEnumerator<" << typeS
1802                         << "> e = " << param << ".GetEnumerator();";
1803                     out << nl << "while(e.MoveNext())";
1804                     out << sb;
1805                     out << nl << stream << ".writeEnum((int)e.Current, " << en->maxValue() << ");";
1806                     out << eb;
1807                 }
1808             }
1809             else
1810             {
1811                 out << nl << "for(int ix = 0; ix < " << param << '.' << limitID << "; ++ix)";
1812                 out << sb;
1813                 out << nl << stream << ".writeEnum((int)" << param << "[ix], " << en->maxValue() << ");";
1814                 out << eb;
1815             }
1816             out << eb;
1817         }
1818         else
1819         {
1820             out << sb;
1821             out << nl << "int szx = " << stream << ".readAndCheckSeqSize(" <<
1822                 static_cast<unsigned>(type->minWireSize()) << ");";
1823             if(isArray)
1824             {
1825                 out << nl << param << " = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1826             }
1827             else if(isCustom)
1828             {
1829                 out << nl << param << " = new global::" << genericType << "<" << typeS << ">();";
1830             }
1831             else if(isStack)
1832             {
1833                 out << nl << typeS << "[] " << param << "_tmp = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1834             }
1835             else
1836             {
1837                 out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">(";
1838                 if(!isLinkedList)
1839                 {
1840                     out << "szx";
1841                 }
1842                 out << ");";
1843             }
1844             out << nl << "for(int ix = 0; ix < szx; ++ix)";
1845             out << sb;
1846             if(isArray || isStack)
1847             {
1848                 string v = isArray ? param : param + "_tmp";
1849                 out << nl << v << "[ix] = (" << typeS << ')' << stream << ".readEnum(" << en->maxValue() << ");";
1850             }
1851             else
1852             {
1853                 out << nl << param << "." << addMethod << "((" << typeS << ')' << stream << ".readEnum("
1854                     << en->maxValue() << "));";
1855             }
1856             out << eb;
1857             if(isStack)
1858             {
1859                 out << nl << "global::System.Array.Reverse(" << param << "_tmp);";
1860                 out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">("
1861                     << param << "_tmp);";
1862             }
1863             out << eb;
1864         }
1865         return;
1866     }
1867 
1868     string helperName;
1869     if(ProxyPtr::dynamicCast(type))
1870     {
1871         helperName = getUnqualified(ProxyPtr::dynamicCast(type)->_class(), scope, "", "PrxHelper");
1872     }
1873     else
1874     {
1875         helperName = getUnqualified(ContainedPtr::dynamicCast(type), scope, "", "Helper");
1876     }
1877 
1878     string func;
1879     if(marshal)
1880     {
1881         func = "write";
1882         out << nl << "if(" << param << " == null)";
1883         out << sb;
1884         out << nl << stream << ".writeSize(0);";
1885         out << eb;
1886         out << nl << "else";
1887         out << sb;
1888         out << nl << stream << ".writeSize(" << param << '.' << limitID << ");";
1889         if(isGeneric && !isList)
1890         {
1891             //
1892             // Stacks are marshaled top-down.
1893             //
1894             if(isStack)
1895             {
1896                 out << nl << typeS << "[] " << param << "_tmp = " << param << ".ToArray();";
1897                 out << nl << "for(int ix = 0; ix < " << param << "_tmp.Length; ++ix)";
1898                 out << sb;
1899                 out << nl << helperName << '.' << func << '(' << stream << ", " << param << "_tmp[ix]);";
1900                 out << eb;
1901             }
1902             else
1903             {
1904                 out << nl << "global::System.Collections.Generic.IEnumerator<" << typeS
1905                     << "> e = " << param << ".GetEnumerator();";
1906                 out << nl << "while(e.MoveNext())";
1907                 out << sb;
1908                 out << nl << helperName << '.' << func << '(' << stream << ", e.Current);";
1909                 out << eb;
1910             }
1911         }
1912         else
1913         {
1914             out << nl << "for(int ix = 0; ix < " << param << '.' << limitID << "; ++ix)";
1915             out << sb;
1916             out << nl << helperName << '.' << func << '(' << stream << ", " << param << "[ix]);";
1917             out << eb;
1918         }
1919         out << eb;
1920     }
1921     else
1922     {
1923         func = "read";
1924         out << sb;
1925         out << nl << "int szx = " << stream << ".readAndCheckSeqSize("
1926             << static_cast<unsigned>(type->minWireSize()) << ");";
1927         if(isArray)
1928         {
1929             out << nl << param << " = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1930         }
1931         else if(isCustom)
1932         {
1933             out << nl << param << " = new global::" << genericType << "<" << typeS << ">();";
1934         }
1935         else if(isStack)
1936         {
1937             out << nl << typeS << "[] " << param << "_tmp = new " << toArrayAlloc(typeS + "[]", "szx") << ";";
1938         }
1939         else
1940         {
1941             out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">();";
1942         }
1943         out << nl << "for(int ix = 0; ix < szx; ++ix)";
1944         out << sb;
1945         if(isArray || isStack)
1946         {
1947             string v = isArray ? param : param + "_tmp";
1948             out << nl << v << "[ix] = " << helperName << '.' << func << '(' << stream << ");";
1949         }
1950         else
1951         {
1952             out << nl << param << "." << addMethod << "(" << helperName << '.' << func << '(' << stream << "));";
1953         }
1954         out << eb;
1955         if(isStack)
1956         {
1957             out << nl << "global::System.Array.Reverse(" << param << "_tmp);";
1958             out << nl << param << " = new global::System.Collections.Generic." << genericType << "<" << typeS << ">("
1959                 << param << "_tmp);";
1960         }
1961         out << eb;
1962     }
1963 
1964     return;
1965 }
1966 
1967 void
writeOptionalSequenceMarshalUnmarshalCode(Output & out,const SequencePtr & seq,const string & scope,const string & param,int tag,bool marshal,const string & customStream)1968 Slice::CsGenerator::writeOptionalSequenceMarshalUnmarshalCode(Output& out,
1969                                                               const SequencePtr& seq,
1970                                                               const string& scope,
1971                                                               const string& param,
1972                                                               int tag,
1973                                                               bool marshal,
1974                                                               const string& customStream)
1975 {
1976     string stream = customStream;
1977     if(stream.empty())
1978     {
1979         stream = marshal ? "ostr" : "istr";
1980     }
1981 
1982     const TypePtr type = seq->type();
1983     const string typeS = typeToString(type, scope);
1984     const string seqS = typeToString(seq, scope);
1985 
1986     string meta;
1987     const bool isArray = !seq->findMetaData("cs:generic:", meta);
1988     const string length = isArray ? param + ".Value.Length" : param + ".Value.Count";
1989 
1990     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
1991     if(builtin)
1992     {
1993         switch(builtin->kind())
1994         {
1995         case Builtin::KindByte:
1996         case Builtin::KindBool:
1997         case Builtin::KindShort:
1998         case Builtin::KindInt:
1999         case Builtin::KindFloat:
2000         case Builtin::KindLong:
2001         case Builtin::KindDouble:
2002         case Builtin::KindString:
2003         {
2004             string func = typeS;
2005             func[0] = static_cast<char>(toupper(static_cast<unsigned char>(typeS[0])));
2006             const bool isSerializable = seq->findMetaData("cs:serializable:", meta);
2007 
2008             if(marshal)
2009             {
2010                 if(isSerializable)
2011                 {
2012                     out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag
2013                         << ", " << getUnqualified("Ice.OptionalFormat", scope) << ".VSize))";
2014                     out << sb;
2015                     out << nl << stream << ".writeSerializable(" << param << ".Value);";
2016                     out << eb;
2017                 }
2018                 else if(isArray)
2019                 {
2020                     out << nl << stream << ".write" << func << "Seq(" << tag << ", " << param << ");";
2021                 }
2022                 else
2023                 {
2024                     out << nl << "if(" << param << ".HasValue)";
2025                     out << sb;
2026                     out << nl << stream << ".write" << func << "Seq(" << tag << ", " << param
2027                         << ".Value == null ? 0 : " << param << ".Value.Count, " << param << ".Value);";
2028                     out << eb;
2029                 }
2030             }
2031             else
2032             {
2033                 out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(seq, scope) << "))";
2034                 out << sb;
2035                 if(builtin->isVariableLength())
2036                 {
2037                     out << nl << stream << ".skip(4);";
2038                 }
2039                 else if(builtin->kind() != Builtin::KindByte && builtin->kind() != Builtin::KindBool)
2040                 {
2041                     out << nl << stream << ".skipSize();";
2042                 }
2043                 string tmp = "tmpVal";
2044                 out << nl << seqS << ' ' << tmp << ';';
2045                 writeSequenceMarshalUnmarshalCode(out, seq, scope, tmp, marshal, true, stream);
2046                 out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">(" << tmp
2047                     << ");";
2048                 out << eb;
2049                 out << nl << "else";
2050                 out << sb;
2051                 out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">();";
2052                 out << eb;
2053             }
2054             break;
2055         }
2056 
2057         case Builtin::KindValue:
2058         case Builtin::KindObject:
2059         case Builtin::KindObjectProxy:
2060         {
2061             if(marshal)
2062             {
2063                 out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag << ", "
2064                     << getOptionalFormat(seq, scope) << "))";
2065                 out << sb;
2066                 out << nl << "int pos = " << stream << ".startSize();";
2067                 writeSequenceMarshalUnmarshalCode(out, seq, scope, param + ".Value", marshal, true, stream);
2068                 out << nl << stream << ".endSize(pos);";
2069                 out << eb;
2070             }
2071             else
2072             {
2073                 out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(seq, scope) << "))";
2074                 out << sb;
2075                 out << nl << stream << ".skip(4);";
2076                 string tmp = "tmpVal";
2077                 out << nl << seqS << ' ' << tmp << ';';
2078                 writeSequenceMarshalUnmarshalCode(out, seq, scope, tmp, marshal, true, stream);
2079                 out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">(" << tmp
2080                     << ");";
2081                 out << eb;
2082                 out << nl << "else";
2083                 out << sb;
2084                 out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">();";
2085                 out << eb;
2086             }
2087             break;
2088         }
2089 
2090         case Builtin::KindLocalObject:
2091             assert(false);
2092         }
2093 
2094         return;
2095     }
2096 
2097     StructPtr st = StructPtr::dynamicCast(type);
2098     if(st)
2099     {
2100         if(marshal)
2101         {
2102             out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag << ", "
2103                 << getOptionalFormat(seq, scope) << "))";
2104             out << sb;
2105             if(st->isVariableLength())
2106             {
2107                 out << nl << "int pos = " << stream << ".startSize();";
2108             }
2109             else if(st->minWireSize() > 1)
2110             {
2111                 out << nl << stream << ".writeSize(" << param << ".Value == null ? 1 : " << length << " * "
2112                     << st->minWireSize() << " + (" << length << " > 254 ? 5 : 1));";
2113             }
2114             writeSequenceMarshalUnmarshalCode(out, seq, scope, param + ".Value", marshal, true, stream);
2115             if(st->isVariableLength())
2116             {
2117                 out << nl << stream << ".endSize(pos);";
2118             }
2119             out << eb;
2120         }
2121         else
2122         {
2123             out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(seq, scope) << "))";
2124             out << sb;
2125             if(st->isVariableLength())
2126             {
2127                 out << nl << stream << ".skip(4);";
2128             }
2129             else if(st->minWireSize() > 1)
2130             {
2131                 out << nl << stream << ".skipSize();";
2132             }
2133             string tmp = "tmpVal";
2134             out << nl << seqS << ' ' << tmp << ';';
2135             writeSequenceMarshalUnmarshalCode(out, seq, scope, tmp, marshal, true, stream);
2136             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">(" << tmp << ");";
2137             out << eb;
2138             out << nl << "else";
2139             out << sb;
2140             out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">();";
2141             out << eb;
2142         }
2143         return;
2144     }
2145 
2146     //
2147     // At this point, all remaining element types have variable size.
2148     //
2149     if(marshal)
2150     {
2151         out << nl << "if(" << param << ".HasValue && " << stream << ".writeOptional(" << tag << ", "
2152             << getOptionalFormat(seq, scope) << "))";
2153         out << sb;
2154         out << nl << "int pos = " << stream << ".startSize();";
2155         writeSequenceMarshalUnmarshalCode(out, seq, scope, param + ".Value", marshal, true, stream);
2156         out << nl << stream << ".endSize(pos);";
2157         out << eb;
2158     }
2159     else
2160     {
2161         out << nl << "if(" << stream << ".readOptional(" << tag << ", " << getOptionalFormat(seq, scope) << "))";
2162         out << sb;
2163         out << nl << stream << ".skip(4);";
2164         string tmp = "tmpVal";
2165         out << nl << seqS << ' ' << tmp << ';';
2166         writeSequenceMarshalUnmarshalCode(out, seq, scope, tmp, marshal, true, stream);
2167         out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">(" << tmp << ");";
2168         out << eb;
2169         out << nl << "else";
2170         out << sb;
2171         out << nl << param << " = new " << getUnqualified("Ice.Optional", scope) << "<" << seqS << ">();";
2172         out << eb;
2173     }
2174 }
2175 
2176 void
writeSerializeDeserializeCode(Output & out,const TypePtr & type,const string & scope,const string & param,bool optional,int,bool serialize)2177 Slice::CsGenerator::writeSerializeDeserializeCode(Output &out,
2178                                                   const TypePtr& type,
2179                                                   const string& scope,
2180                                                   const string& param,
2181                                                   bool optional,
2182                                                   int /*tag*/,
2183                                                   bool serialize)
2184 {
2185     //
2186     // Could do it only when param == "info", but not as good for testing
2187     //
2188     string dataMember = "this." + param;
2189     if(optional)
2190     {
2191         const string typeName = typeToString(type, scope, true);
2192         if(serialize)
2193         {
2194             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2195         }
2196         else
2197         {
2198             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2199                 << "));";
2200         }
2201         return;
2202     }
2203 
2204     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
2205     if(builtin)
2206     {
2207         switch(builtin->kind())
2208         {
2209             case Builtin::KindByte:
2210             {
2211                 if(serialize)
2212                 {
2213                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2214                 }
2215                 else
2216                 {
2217                     out << nl << dataMember << " = " << "info.GetByte(\"" << param << "\");";
2218                 }
2219                 break;
2220             }
2221             case Builtin::KindBool:
2222             {
2223                 if(serialize)
2224                 {
2225                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2226                 }
2227                 else
2228                 {
2229                     out << nl << dataMember << " = " << "info.GetBoolean(\"" << param << "\");";
2230                 }
2231                 break;
2232             }
2233             case Builtin::KindShort:
2234             {
2235                 if(serialize)
2236                 {
2237                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2238                 }
2239                 else
2240                 {
2241                     out << nl << dataMember << " = " << "info.GetInt16(\"" << param << "\");";
2242                 }
2243                 break;
2244             }
2245             case Builtin::KindInt:
2246             {
2247                 if(serialize)
2248                 {
2249                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2250                 }
2251                 else
2252                 {
2253                     out << nl << dataMember << " = " << "info.GetInt32(\"" << param << "\");";
2254                 }
2255                 break;
2256             }
2257             case Builtin::KindLong:
2258             {
2259                 if(serialize)
2260                 {
2261                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2262                 }
2263                 else
2264                 {
2265                     out << nl << dataMember << " = " << "info.GetInt64(\"" << param << "\");";
2266                 }
2267                 break;
2268             }
2269             case Builtin::KindFloat:
2270             {
2271                 if(serialize)
2272                 {
2273                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2274                 }
2275                 else
2276                 {
2277                     out << nl << dataMember << " = " << "info.GetSingle(\"" << param << "\");";
2278                 }
2279                 break;
2280             }
2281             case Builtin::KindDouble:
2282             {
2283                 if(serialize)
2284                 {
2285                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ");";
2286                 }
2287                 else
2288                 {
2289                     out << nl << dataMember << " = " << "info.GetDouble(\"" << param << "\");";
2290                 }
2291                 break;
2292             }
2293             case Builtin::KindString:
2294             {
2295                 if(serialize)
2296                 {
2297                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << " == null ? \"\" : "
2298                         << dataMember << ");";
2299                 }
2300                 else
2301                 {
2302                     out << nl << dataMember << " = " << "info.GetString(\"" << param << "\");";
2303                 }
2304                 break;
2305             }
2306             case Builtin::KindValue:
2307             case Builtin::KindObject:
2308             case Builtin::KindLocalObject:
2309             {
2310                 const string typeName = typeToString(type, scope, false);
2311                 if(serialize)
2312                 {
2313                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2314                 }
2315                 else
2316                 {
2317                     out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof("
2318                         << typeName << "));";
2319                 }
2320                 break;
2321             }
2322             case Builtin::KindObjectProxy:
2323             {
2324                 if(serialize)
2325                 {
2326                     out << nl << "info.AddValue(\"" << param << "\", " << dataMember
2327                         << ", typeof(Ice.ObjectPrxHelperBase));";
2328                 }
2329                 else
2330                 {
2331                     out << nl << dataMember << " = (Ice.ObjectPrx)info.GetValue(\"" << param
2332                         << "\", typeof(Ice.ObjectPrxHelperBase));";
2333                 }
2334                 break;
2335             }
2336         }
2337         return;
2338     }
2339 
2340     ProxyPtr prx = ProxyPtr::dynamicCast(type);
2341     if(prx)
2342     {
2343         const string typeName = typeToString(type, scope, false);
2344         if(serialize)
2345         {
2346             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "Helper));";
2347         }
2348         else
2349         {
2350             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2351                 << "Helper));";
2352         }
2353         return;
2354     }
2355 
2356     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
2357     if(cl)
2358     {
2359         const string typeName = typeToString(type, scope, false);
2360         if(serialize)
2361         {
2362             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2363         }
2364         else
2365         {
2366             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2367                 << "));";
2368         }
2369         return;
2370     }
2371 
2372     StructPtr st = StructPtr::dynamicCast(type);
2373     if(st)
2374     {
2375         const string typeName = typeToString(type, scope, false);
2376         if(serialize)
2377         {
2378             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2379         }
2380         else
2381         {
2382             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2383                 << "));";
2384         }
2385         return;
2386     }
2387 
2388     EnumPtr en = EnumPtr::dynamicCast(type);
2389     if(en)
2390     {
2391         const string typeName = typeToString(type, scope, false);
2392         if(serialize)
2393         {
2394             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2395         }
2396         else
2397         {
2398             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2399                 << "));";
2400         }
2401         return;
2402     }
2403 
2404     SequencePtr seq = SequencePtr::dynamicCast(type);
2405     if(seq)
2406     {
2407         const string typeName = typeToString(type, scope, false);
2408         if(serialize)
2409         {
2410             out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2411         }
2412         else
2413         {
2414             out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2415                 << "));";
2416         }
2417         return;
2418     }
2419 
2420     DictionaryPtr d = DictionaryPtr::dynamicCast(type);
2421     assert(d);
2422     const string typeName = typeToString(type, scope, false);
2423     if(serialize)
2424     {
2425         out << nl << "info.AddValue(\"" << param << "\", " << dataMember << ", typeof(" << typeName << "));";
2426     }
2427     else
2428     {
2429         out << nl << dataMember << " = (" << typeName << ")info.GetValue(\"" << param << "\", typeof(" << typeName
2430             << "));";
2431     }
2432 }
2433 
2434 string
toArrayAlloc(const string & decl,const string & sz)2435 Slice::CsGenerator::toArrayAlloc(const string& decl, const string& sz)
2436 {
2437     int count = 0;
2438     string::size_type pos = decl.size();
2439     while(pos > 1 && decl.substr(pos - 2, 2) == "[]")
2440     {
2441         ++count;
2442         pos -= 2;
2443     }
2444     assert(count > 0);
2445 
2446     ostringstream o;
2447     o << decl.substr(0, pos) << '[' << sz << ']' << decl.substr(pos + 2);
2448     return o.str();
2449 }
2450 
2451 void
validateMetaData(const UnitPtr & u)2452 Slice::CsGenerator::validateMetaData(const UnitPtr& u)
2453 {
2454     MetaDataVisitor visitor;
2455     u->visit(&visitor, true);
2456 }
2457 
2458 bool
visitUnitStart(const UnitPtr & p)2459 Slice::CsGenerator::MetaDataVisitor::visitUnitStart(const UnitPtr& p)
2460 {
2461     //
2462     // Validate global metadata in the top-level file and all included files.
2463     //
2464     StringList files = p->allFiles();
2465     for(StringList::iterator q = files.begin(); q != files.end(); ++q)
2466     {
2467         string file = *q;
2468         DefinitionContextPtr dc = p->findDefinitionContext(file);
2469         assert(dc);
2470         StringList globalMetaData = dc->getMetaData();
2471         StringList newGlobalMetaData;
2472         static const string csPrefix = "cs:";
2473         static const string clrPrefix = "clr:";
2474 
2475         for(StringList::iterator r = globalMetaData.begin(); r != globalMetaData.end(); ++r)
2476         {
2477             string& s = *r;
2478             string oldS = s;
2479 
2480             if(s.find(clrPrefix) == 0)
2481             {
2482                 s.replace(0, clrPrefix.size(), csPrefix);
2483             }
2484 
2485             if(s.find(csPrefix) == 0)
2486             {
2487                 static const string csAttributePrefix = csPrefix + "attribute:";
2488                 static const string csTypeIdNsPrefix = csPrefix + "typeid-namespace:";
2489                 if(!(s.find(csTypeIdNsPrefix) == 0 && s.size() > csTypeIdNsPrefix.size()) &&
2490                    !(s.find(csAttributePrefix) == 0 && s.size() > csAttributePrefix.size()))
2491                 {
2492                     dc->warning(InvalidMetaData, file, -1, "ignoring invalid global metadata `" + oldS + "'");
2493                     continue;
2494                 }
2495             }
2496             newGlobalMetaData.push_back(oldS);
2497         }
2498 
2499         dc->setMetaData(newGlobalMetaData);
2500     }
2501     return true;
2502 }
2503 
2504 bool
visitModuleStart(const ModulePtr & p)2505 Slice::CsGenerator::MetaDataVisitor::visitModuleStart(const ModulePtr& p)
2506 {
2507     validate(p);
2508     return true;
2509 }
2510 
2511 void
visitModuleEnd(const ModulePtr &)2512 Slice::CsGenerator::MetaDataVisitor::visitModuleEnd(const ModulePtr&)
2513 {
2514 }
2515 
2516 void
visitClassDecl(const ClassDeclPtr & p)2517 Slice::CsGenerator::MetaDataVisitor::visitClassDecl(const ClassDeclPtr& p)
2518 {
2519     validate(p);
2520 }
2521 
2522 bool
visitClassDefStart(const ClassDefPtr & p)2523 Slice::CsGenerator::MetaDataVisitor::visitClassDefStart(const ClassDefPtr& p)
2524 {
2525     validate(p);
2526     return true;
2527 }
2528 
2529 void
visitClassDefEnd(const ClassDefPtr &)2530 Slice::CsGenerator::MetaDataVisitor::visitClassDefEnd(const ClassDefPtr&)
2531 {
2532 }
2533 
2534 bool
visitExceptionStart(const ExceptionPtr & p)2535 Slice::CsGenerator::MetaDataVisitor::visitExceptionStart(const ExceptionPtr& p)
2536 {
2537     validate(p);
2538     return true;
2539 }
2540 
2541 void
visitExceptionEnd(const ExceptionPtr &)2542 Slice::CsGenerator::MetaDataVisitor::visitExceptionEnd(const ExceptionPtr&)
2543 {
2544 }
2545 
2546 bool
visitStructStart(const StructPtr & p)2547 Slice::CsGenerator::MetaDataVisitor::visitStructStart(const StructPtr& p)
2548 {
2549     validate(p);
2550     return true;
2551 }
2552 
2553 void
visitStructEnd(const StructPtr &)2554 Slice::CsGenerator::MetaDataVisitor::visitStructEnd(const StructPtr&)
2555 {
2556 }
2557 
2558 void
visitOperation(const OperationPtr & p)2559 Slice::CsGenerator::MetaDataVisitor::visitOperation(const OperationPtr& p)
2560 {
2561     validate(p);
2562 
2563     ParamDeclList params = p->parameters();
2564     for(ParamDeclList::const_iterator i = params.begin(); i != params.end(); ++i)
2565     {
2566         visitParamDecl(*i);
2567     }
2568 }
2569 
2570 void
visitParamDecl(const ParamDeclPtr & p)2571 Slice::CsGenerator::MetaDataVisitor::visitParamDecl(const ParamDeclPtr& p)
2572 {
2573     validate(p);
2574 }
2575 
2576 void
visitDataMember(const DataMemberPtr & p)2577 Slice::CsGenerator::MetaDataVisitor::visitDataMember(const DataMemberPtr& p)
2578 {
2579     validate(p);
2580 }
2581 
2582 void
visitSequence(const SequencePtr & p)2583 Slice::CsGenerator::MetaDataVisitor::visitSequence(const SequencePtr& p)
2584 {
2585     validate(p);
2586 }
2587 
2588 void
visitDictionary(const DictionaryPtr & p)2589 Slice::CsGenerator::MetaDataVisitor::visitDictionary(const DictionaryPtr& p)
2590 {
2591     validate(p);
2592 }
2593 
2594 void
visitEnum(const EnumPtr & p)2595 Slice::CsGenerator::MetaDataVisitor::visitEnum(const EnumPtr& p)
2596 {
2597     validate(p);
2598 }
2599 
2600 void
visitConst(const ConstPtr & p)2601 Slice::CsGenerator::MetaDataVisitor::visitConst(const ConstPtr& p)
2602 {
2603     validate(p);
2604 }
2605 
2606 void
validate(const ContainedPtr & cont)2607 Slice::CsGenerator::MetaDataVisitor::validate(const ContainedPtr& cont)
2608 {
2609     const string msg = "ignoring invalid metadata";
2610 
2611     StringList localMetaData = cont->getMetaData();
2612     StringList newLocalMetaData;
2613 
2614     const UnitPtr ut = cont->unit();
2615     const DefinitionContextPtr dc = ut->findDefinitionContext(cont->file());
2616     assert(dc);
2617 
2618     for(StringList::iterator p = localMetaData.begin(); p != localMetaData.end(); ++p)
2619     {
2620         string& s = *p;
2621         string oldS = s;
2622 
2623         const string csPrefix = "cs:";
2624         const string clrPrefix = "clr:";
2625 
2626         if(s.find(clrPrefix) == 0)
2627         {
2628             s.replace(0, clrPrefix.size(), csPrefix);
2629         }
2630 
2631         if(s.find(csPrefix) == 0)
2632         {
2633             SequencePtr seq = SequencePtr::dynamicCast(cont);
2634             if(seq)
2635             {
2636                 static const string csGenericPrefix = csPrefix + "generic:";
2637                 if(s.find(csGenericPrefix) == 0)
2638                 {
2639                     string type = s.substr(csGenericPrefix.size());
2640                     if(type == "LinkedList" || type == "Queue" || type == "Stack")
2641                     {
2642                         if(!isClassType(seq->type()))
2643                         {
2644                             newLocalMetaData.push_back(s);
2645                             continue;
2646                         }
2647                     }
2648                     else if(!type.empty())
2649                     {
2650                         newLocalMetaData.push_back(s);
2651                         continue; // Custom type or List<T>
2652                     }
2653                 }
2654                 static const string csSerializablePrefix = csPrefix + "serializable:";
2655                 if(s.find(csSerializablePrefix) == 0)
2656                 {
2657                     string meta;
2658                     if(cont->findMetaData(csPrefix + "generic:", meta))
2659                     {
2660                         dc->warning(InvalidMetaData, cont->file(), cont->line(), msg + " `" + meta + "':\n" +
2661                                     "serialization can only be used with the array mapping for byte sequences");
2662                         continue;
2663                     }
2664                     string type = s.substr(csSerializablePrefix.size());
2665                     BuiltinPtr builtin = BuiltinPtr::dynamicCast(seq->type());
2666                     if(!type.empty() && builtin && builtin->kind() == Builtin::KindByte)
2667                     {
2668                         newLocalMetaData.push_back(s);
2669                         continue;
2670                     }
2671                 }
2672             }
2673             else if(StructPtr::dynamicCast(cont))
2674             {
2675                 if(s.substr(csPrefix.size()) == "class")
2676                 {
2677                     newLocalMetaData.push_back(s);
2678                     continue;
2679                 }
2680                 if(s.substr(csPrefix.size()) == "property")
2681                 {
2682                     newLocalMetaData.push_back(s);
2683                     continue;
2684                 }
2685                 static const string csImplementsPrefix = csPrefix + "implements:";
2686                 if(s.find(csImplementsPrefix) == 0)
2687                 {
2688                     newLocalMetaData.push_back(s);
2689                     continue;
2690                 }
2691             }
2692             else if(ClassDefPtr::dynamicCast(cont))
2693             {
2694                 if(s.substr(csPrefix.size()) == "property")
2695                 {
2696                     newLocalMetaData.push_back(s);
2697                     continue;
2698                 }
2699                 static const string csImplementsPrefix = csPrefix + "implements:";
2700                 if(s.find(csImplementsPrefix) == 0)
2701                 {
2702                     newLocalMetaData.push_back(s);
2703                     continue;
2704                 }
2705             }
2706             else if(DictionaryPtr::dynamicCast(cont))
2707             {
2708                 static const string csGenericPrefix = csPrefix + "generic:";
2709                 if(s.find(csGenericPrefix) == 0)
2710                 {
2711                     string type = s.substr(csGenericPrefix.size());
2712                     if(type == "SortedDictionary" ||  type == "SortedList")
2713                     {
2714                         newLocalMetaData.push_back(s);
2715                         continue;
2716                     }
2717                 }
2718             }
2719             else if(DataMemberPtr::dynamicCast(cont))
2720             {
2721                 DataMemberPtr dataMember = DataMemberPtr::dynamicCast(cont);
2722                 StructPtr st = StructPtr::dynamicCast(dataMember->container());
2723                 ExceptionPtr ex = ExceptionPtr::dynamicCast(dataMember->container());
2724                 ClassDefPtr cl = ClassDefPtr::dynamicCast(dataMember->container());
2725                 bool isLocal = (st && st->isLocal()) || (ex && ex->isLocal()) || (cl && cl->isLocal());
2726                 static const string csTypePrefix = csPrefix + "type:";
2727                 if(isLocal && s.find(csTypePrefix) == 0)
2728                 {
2729                     newLocalMetaData.push_back(s);
2730                     continue;
2731                 }
2732             }
2733             else if(ModulePtr::dynamicCast(cont))
2734             {
2735                 static const string csNamespacePrefix = csPrefix + "namespace:";
2736                 if(s.find(csNamespacePrefix) == 0 && s.size() > csNamespacePrefix.size())
2737                 {
2738                     newLocalMetaData.push_back(s);
2739                     continue;
2740                 }
2741             }
2742 
2743             static const string csAttributePrefix = csPrefix + "attribute:";
2744             static const string csTie = csPrefix + "tie";
2745             if(s.find(csAttributePrefix) == 0 && s.size() > csAttributePrefix.size())
2746             {
2747                 newLocalMetaData.push_back(s);
2748                 continue;
2749             }
2750             else if(s.find(csTie) == 0 && s.size() == csTie.size())
2751             {
2752                 newLocalMetaData.push_back(s);
2753                 continue;
2754             }
2755 
2756             dc->warning(InvalidMetaData, cont->file(), cont->line(), msg + " `" + oldS + "'");
2757             continue;
2758         }
2759         else if(s == "delegate")
2760         {
2761             ClassDefPtr cl = ClassDefPtr::dynamicCast(cont);
2762             if(cl && cl->isDelegate())
2763             {
2764                 newLocalMetaData.push_back(s);
2765                 continue;
2766             }
2767 
2768             dc->warning(InvalidMetaData, cont->file(), cont->line(), msg + " `" + s + "'");
2769             continue;
2770         }
2771         newLocalMetaData.push_back(s);
2772     }
2773 
2774     cont->setMetaData(newLocalMetaData);
2775 }
2776