1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <JsUtil.h>
6 #include <Slice/Util.h>
7 #include <IceUtil/Functional.h>
8 #include <IceUtil/StringUtil.h>
9 
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 
13 #ifdef _WIN32
14 #include <direct.h>
15 #endif
16 
17 #ifndef _WIN32
18 #include <unistd.h>
19 #endif
20 
21 // TODO: fix this warning!
22 #if defined(_MSC_VER)
23 #   pragma warning(disable:4456) // shadow
24 #   pragma warning(disable:4457) // shadow
25 #   pragma warning(disable:4459) // shadow
26 #elif defined(__clang__)
27 #   pragma clang diagnostic ignored "-Wshadow"
28 #elif defined(__GNUC__)
29 #   pragma GCC diagnostic ignored "-Wshadow"
30 #endif
31 
32 using namespace std;
33 using namespace Slice;
34 using namespace IceUtil;
35 using namespace IceUtilInternal;
36 
37 string
relativePath(const string & p1,const string & p2)38 Slice::relativePath(const string& p1, const string& p2)
39 {
40     vector<string> tokens1;
41     vector<string> tokens2;
42 
43     splitString(p1, "/\\", tokens1);
44     splitString(p2, "/\\", tokens2);
45 
46     string f1 = tokens1.back();
47     string f2 = tokens2.back();
48 
49     tokens1.pop_back();
50     tokens2.pop_back();
51 
52     vector<string>::const_iterator i1 = tokens1.begin();
53     vector<string>::const_iterator i2 = tokens2.begin();
54 
55     while(i1 != tokens1.end() && i2 != tokens2.end() && *i1 == *i2)
56     {
57         i1++;
58         i2++;
59     }
60 
61     //
62     // Different volumes, relative path not possible.
63     //
64     if(i1 == tokens1.begin() && i2 == tokens2.begin())
65     {
66         return p1;
67     }
68 
69     string newPath;
70     if(i2 == tokens2.end())
71     {
72         newPath += "./";
73         for (; i1 != tokens1.end(); ++i1)
74         {
75             newPath += *i1 + "/";
76         }
77     }
78     else
79     {
80         for(size_t i = tokens2.end() - i2; i > 0; i--)
81         {
82             newPath += "../";
83         }
84 
85         for(; i1 != tokens1.end(); ++i1)
86         {
87             newPath += *i1 + "/";
88         }
89     }
90     newPath += f1;
91 
92     return newPath;
93 }
94 
95 static string
lookupKwd(const string & name)96 lookupKwd(const string& name)
97 {
98     //
99     // Keyword list. *Must* be kept in alphabetical order.
100     //
101     static const string keywordList[] =
102     {
103         "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
104         "else", "enum", "export", "extends", "false", "finally", "for", "function", "if", "implements", "import",
105         "in", "instanceof", "interface", "let", "new", "null", "package", "private", "protected", "public", "return",
106         "static", "super", "switch", "this", "throw", "true", "try", "typeof", "var", "void", "while", "with",
107         "yield"
108     };
109     bool found = binary_search(&keywordList[0],
110                                &keywordList[sizeof(keywordList) / sizeof(*keywordList)],
111                                name,
112                                Slice::CICompare());
113     if(found)
114     {
115         return "_" + name;
116     }
117 
118     return name;
119 }
120 
121 //
122 // Split a scoped name into its components and return the components as a list of (unscoped) identifiers.
123 //
124 static StringList
splitScopedName(const string & scoped)125 splitScopedName(const string& scoped)
126 {
127     assert(scoped[0] == ':');
128     StringList ids;
129     string::size_type next = 0;
130     string::size_type pos;
131     while((pos = scoped.find("::", next)) != string::npos)
132     {
133         pos += 2;
134         if(pos != scoped.size())
135         {
136             string::size_type endpos = scoped.find("::", pos);
137             if(endpos != string::npos)
138             {
139                 ids.push_back(scoped.substr(pos, endpos - pos));
140             }
141         }
142         next = pos;
143     }
144     if(next != scoped.size())
145     {
146         ids.push_back(scoped.substr(next));
147     }
148     else
149     {
150         ids.push_back("");
151     }
152 
153     return ids;
154 }
155 
156 static StringList
fixIds(const StringList & ids)157 fixIds(const StringList& ids)
158 {
159     StringList newIds;
160     for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i)
161     {
162         newIds.push_back(lookupKwd(*i));
163     }
164     return newIds;
165 }
166 
167 string
getModuleMetadata(const TypePtr & type)168 Slice::JsGenerator::getModuleMetadata(const TypePtr& type)
169 {
170     static const char* builtinModuleTable[] =
171     {
172         "",           // byte
173         "",           // bool
174         "",           // short
175         "",           // int
176         "ice",        // long
177         "",           // float
178         "",           // double
179         "",           // string
180         "ice",        // Ice.Value
181         "ice",        // Ice.ObjectPrx
182         "",           // LocalObject
183         "ice"         // Ice.Object
184     };
185 
186     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
187     if(builtin)
188     {
189         return builtinModuleTable[builtin->kind()];
190     }
191 
192     ProxyPtr proxy = ProxyPtr::dynamicCast(type);
193     return getModuleMetadata(proxy ? ContainedPtr::dynamicCast(proxy->_class()->definition()) :
194                                      ContainedPtr::dynamicCast(type));
195 }
196 
197 string
getModuleMetadata(const ContainedPtr & p)198 Slice::JsGenerator::getModuleMetadata(const ContainedPtr& p)
199 {
200     //
201     // Check if the file contains the python:pkgdir global metadata.
202     //
203     DefinitionContextPtr dc = p->definitionContext();
204     assert(dc);
205     const string prefix = "js:module:";
206     const string value = dc->findMetaData(prefix);
207     return value.empty() ? value : value.substr(prefix.size());
208 }
209 
210 bool
isClassType(const TypePtr & type)211 Slice::JsGenerator::isClassType(const TypePtr& type)
212 {
213     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
214     return (builtin && (builtin->kind() == Builtin::KindObject || builtin->kind() == Builtin::KindValue)) ||
215         ClassDeclPtr::dynamicCast(type);
216 }
217 
218 //
219 // If the passed name is a scoped name, return the identical scoped name,
220 // but with all components that are JS keywords replaced by
221 // their "_"-prefixed version; otherwise, if the passed name is
222 // not scoped, but a JS keyword, return the "_"-prefixed name.
223 //
224 string
fixId(const string & name)225 Slice::JsGenerator::fixId(const string& name)
226 {
227     if(name.empty())
228     {
229         return name;
230     }
231     if(name[0] != ':')
232     {
233         return lookupKwd(name);
234     }
235 
236     const StringList ids = splitScopedName(name);
237     const StringList newIds = fixIds(ids);
238 
239     stringstream result;
240     for(StringList::const_iterator j = newIds.begin(); j != newIds.end(); ++j)
241     {
242         if(j != newIds.begin())
243         {
244             result << '.';
245         }
246         result << *j;
247     }
248     return result.str();
249 }
250 
251 string
fixId(const ContainedPtr & cont)252 Slice::JsGenerator::fixId(const ContainedPtr& cont)
253 {
254     return fixId(cont->name());
255 }
256 
257 string
importPrefix(const TypePtr & type,const ContainedPtr & toplevel,const vector<pair<string,string>> & imports)258 Slice::JsGenerator::importPrefix(const TypePtr& type,
259                                  const ContainedPtr& toplevel,
260                                  const vector<pair<string, string> >& imports)
261 {
262     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
263     if(builtin)
264     {
265         return typeToString(type, toplevel, imports, true);
266     }
267     else if(ProxyPtr::dynamicCast(type))
268     {
269         ProxyPtr proxy = ProxyPtr::dynamicCast(type);
270         return importPrefix(ContainedPtr::dynamicCast(proxy->_class()->definition()), toplevel, imports);
271     }
272     else if(ContainedPtr::dynamicCast(type))
273     {
274         bool local = false;
275         if(toplevel)
276         {
277             if(ConstructedPtr::dynamicCast(toplevel))
278             {
279                 local = ConstructedPtr::dynamicCast(toplevel)->isLocal();
280             }
281             else if(ClassDefPtr::dynamicCast(toplevel))
282             {
283                 local = ClassDefPtr::dynamicCast(toplevel)->isLocal();
284             }
285         }
286 
287         ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
288         if(cl && cl->isInterface() && !local)
289         {
290             return "iceNS0.";
291         }
292         else
293         {
294             return importPrefix(ContainedPtr::dynamicCast(type), toplevel, imports);
295         }
296     }
297     return "";
298 }
299 
300 string
importPrefix(const ContainedPtr & contained,const ContainedPtr & toplevel,const vector<pair<string,string>> & imports)301 Slice::JsGenerator::importPrefix(const ContainedPtr& contained,
302                                  const ContainedPtr& toplevel,
303                                  const vector<pair<string, string> >& imports)
304 {
305     string m1 = getModuleMetadata(contained);
306     string m2 = getModuleMetadata(toplevel);
307 
308     string p;
309 
310     if(m1.empty())
311     {
312         string p1 = contained->definitionContext()->filename();
313         string p2 = toplevel->definitionContext()->filename();
314 
315         p = relativePath(p1, p2);
316 
317         string::size_type pos = p.rfind('.');
318         if (pos != string::npos)
319         {
320             p.erase(pos);
321         }
322     }
323     else if(m1 == "ice" && m1 != m2)
324     {
325         return "iceNS0.";
326     }
327     else if(m1 != m2)
328     {
329         p = m1;
330     }
331 
332     if(!p.empty())
333     {
334         for(vector<pair<string, string> >::const_iterator i = imports.begin(); i != imports.end(); ++i)
335         {
336             if(i->first == p)
337             {
338                 return i->second + ".";
339             }
340         }
341     }
342 
343     return "";
344 }
345 
346 bool
findMetaData(const string & prefix,const StringList & metaData,string & value)347 Slice::JsGenerator::findMetaData(const string& prefix, const StringList& metaData, string& value)
348 {
349     for(StringList::const_iterator i = metaData.begin(); i != metaData.end(); i++)
350     {
351         string s = *i;
352         if(s.find(prefix) == 0)
353         {
354             value = s.substr(prefix.size());
355             return true;
356         }
357     }
358     return false;
359 }
360 
361 string
importPrefix(const string & type,const ContainedPtr & toplevel)362 Slice::JsGenerator::importPrefix(const string& type, const ContainedPtr& toplevel)
363 {
364     const string module = getModuleMetadata(toplevel);
365     return (type.find("Ice.") == 0 && module != "ice") ? "iceNS0." : "";
366 }
367 
368 string
getUnqualified(const string & type,const string & scope,const string & importPrefix)369 Slice::JsGenerator::getUnqualified(const string& type, const string& scope, const string& importPrefix)
370 {
371     if(importPrefix.empty())
372     {
373         const string localScope = getLocalScope(scope) + ".";
374         if(type.find(localScope) == 0)
375         {
376             string t = type.substr(localScope.size());
377             if(t.find(".") == string::npos)
378             {
379                 return t;
380             }
381         }
382     }
383     return type;
384 }
385 
386 string
typeToString(const TypePtr & type,const ContainedPtr & toplevel,const vector<pair<string,string>> & imports,bool typescript,bool definition)387 Slice::JsGenerator::typeToString(const TypePtr& type,
388                                  const ContainedPtr& toplevel,
389                                  const vector<pair<string, string> >& imports,
390                                  bool typescript,
391                                  bool definition)
392 {
393     if(!type)
394     {
395         return "void";
396     }
397 
398     bool local = false;
399     if(toplevel)
400     {
401         if(ConstructedPtr::dynamicCast(toplevel))
402         {
403             local = ConstructedPtr::dynamicCast(toplevel)->isLocal();
404         }
405         else if(ClassDefPtr::dynamicCast(toplevel))
406         {
407             local = ClassDefPtr::dynamicCast(toplevel)->isLocal();
408         }
409     }
410 
411     static const char* typeScriptBuiltinTable[] =
412     {
413         "number",           // byte
414         "boolean",          // bool
415         "number",           // short
416         "number",           // int
417         "Ice.Long",         // long
418         "number",           // float
419         "number",           // double
420         "string",
421         "Ice.Object",
422         "Ice.ObjectPrx",
423         "Object",
424         "Ice.Value"
425     };
426 
427     static const char* javaScriptBuiltinTable[] =
428     {
429         "Number",           // byte
430         "Boolean",          // bool
431         "Number",           // short
432         "Number",           // int
433         "Ice.Long",         // long
434         "Number",           // float
435         "Number",           // double
436         "String",
437         "Ice.Value",
438         "Ice.ObjectPrx",
439         "Object",
440         "Ice.Value"
441     };
442 
443     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
444     if(builtin)
445     {
446         if(typescript)
447         {
448             int kind = (!local && builtin->kind() == Builtin::KindObject) ? Builtin::KindValue : builtin->kind();
449             ostringstream os;
450             if(getModuleMetadata(type) == "ice" && getModuleMetadata(toplevel) != "ice")
451             {
452                 os << "iceNS0.";
453             }
454             os << getUnqualified(typeScriptBuiltinTable[kind], toplevel->scope(), "iceNS0.");
455             return os.str();
456         }
457         else
458         {
459             return javaScriptBuiltinTable[builtin->kind()];
460         }
461     }
462 
463     ClassDeclPtr cl = ClassDeclPtr::dynamicCast(type);
464     if(cl)
465     {
466         string prefix;
467         ostringstream os;
468         if(typescript)
469         {
470             if(cl->isInterface() && !local)
471             {
472                 prefix = importPrefix("Ice.Value", toplevel);
473             }
474             else
475             {
476                 prefix = importPrefix(ContainedPtr::dynamicCast(cl), toplevel, imports);
477             }
478         }
479         os << prefix;
480         if(!prefix.empty() && typescript)
481         {
482             if(cl->isInterface() && !local)
483             {
484                 os << getUnqualified("Ice.Value", toplevel->scope(), prefix);
485             }
486             else
487             {
488                 os << getUnqualified(fixId(cl->scoped()), toplevel->scope(), prefix);
489             }
490         }
491         else
492         {
493             os << fixId(cl->scoped());
494         }
495         return os.str();
496     }
497 
498     ProxyPtr proxy = ProxyPtr::dynamicCast(type);
499     if(proxy)
500     {
501         ostringstream os;
502         ClassDefPtr def = proxy->_class()->definition();
503         if(!def->isInterface() && def->allOperations().empty())
504         {
505             if(getModuleMetadata(toplevel) != "ice")
506             {
507                 os << "iceNS0.";
508             }
509             os << getUnqualified(typeScriptBuiltinTable[Builtin::KindObjectProxy],
510                                  toplevel->scope(),
511                                  getModuleMetadata(toplevel));
512         }
513         else
514         {
515             string prefix;
516             if(typescript)
517             {
518                 prefix = importPrefix(ContainedPtr::dynamicCast(def), toplevel, imports);
519                 os << prefix;
520             }
521 
522             if(prefix.empty() && typescript)
523             {
524                 os << getUnqualified(fixId(proxy->_class()->scoped() + "Prx"), toplevel->scope(), prefix);
525             }
526             else
527             {
528                 os << fixId(proxy->_class()->scoped() + "Prx");
529             }
530         }
531         return os.str();
532     }
533 
534     if(!typescript || definition)
535     {
536         SequencePtr seq = SequencePtr::dynamicCast(type);
537         if (seq)
538         {
539             BuiltinPtr b = BuiltinPtr::dynamicCast(seq->type());
540             if (b && b->kind() == Builtin::KindByte)
541             {
542                 return "Uint8Array";
543             }
544             else
545             {
546                 return typeToString(seq->type(), toplevel, imports, typescript) + "[]";
547             }
548         }
549 
550         DictionaryPtr d = DictionaryPtr::dynamicCast(type);
551         if(d)
552         {
553             const TypePtr keyType = d->keyType();
554             BuiltinPtr builtin = BuiltinPtr::dynamicCast(keyType);
555             ostringstream os;
556             if ((builtin && builtin->kind() == Builtin::KindLong) || StructPtr::dynamicCast(keyType))
557             {
558                 const string prefix = importPrefix("Ice.HashMap", toplevel);
559                 os << prefix << getUnqualified("Ice.HashMap", toplevel->scope(), prefix);
560             }
561             else
562             {
563                 os << "Map";
564             }
565 
566             if (typescript)
567             {
568                 os << "<"
569                     << typeToString(keyType, toplevel, imports, true) << ", "
570                     << typeToString(d->valueType(), toplevel, imports, true) << ">";
571             }
572             return os.str();
573         }
574     }
575 
576     ContainedPtr contained = ContainedPtr::dynamicCast(type);
577     if(contained)
578     {
579         ostringstream os;
580         string prefix;
581         if(typescript)
582         {
583             prefix = importPrefix(contained, toplevel, imports);
584             os << prefix;
585         }
586 
587         if(prefix.empty() && typescript)
588         {
589             os << getUnqualified(fixId(contained->scoped()), toplevel->scope(), prefix);
590         }
591         else
592         {
593             os << fixId(contained->scoped());
594         }
595         return os.str();
596     }
597 
598     return "???";
599 }
600 
601 string
typeToString(const TypePtr & type,const ContainedPtr & toplevel,const std::vector<std::pair<std::string,std::string>> & imports,bool typeScript,bool definition,bool usealias)602 Slice::JsGenerator::typeToString(const TypePtr& type,
603                                  const ContainedPtr& toplevel,
604                                  const std::vector<std::pair<std::string, std::string> >& imports,
605                                  bool typeScript,
606                                  bool definition,
607                                  bool usealias)
608 {
609     string t = typeToString(type, toplevel, imports, typeScript, definition);
610     if(usealias)
611     {
612         string m1 = getModuleMetadata(type);
613         string m2 = getModuleMetadata(toplevel);
614         if (!m1.empty() && m1 == m2)
615         {
616             // we are using the same module
617             return t;
618         }
619         string p = importPrefix(type, toplevel, imports);
620 
621         //
622         // When using an import prefix we don't need an alias, prefixes use iceNSXX that is reserved
623         // name prefix
624         //
625         string::size_type i = t.find(".");
626         if(p.empty() && i != string::npos)
627         {
628             const string scoped = fixId(toplevel->scoped()) + ".";
629             if(scoped.find("." + t.substr(0, i + 1)) != string::npos)
630             {
631                 replace(t.begin(), t.end(), '.', '_');
632                 t = "iceA_" + t;
633             }
634         }
635     }
636     return t;
637 }
638 
639 string
getLocalScope(const string & scope,const string & separator)640 Slice::JsGenerator::getLocalScope(const string& scope, const string& separator)
641 {
642     assert(!scope.empty());
643 
644     //
645     // Remove trailing "::" if present.
646     //
647     string fixedScope;
648     if(scope[scope.size() - 1] == ':')
649     {
650         assert(scope[scope.size() - 2] == ':');
651         fixedScope = scope.substr(0, scope.size() - 2);
652     }
653     else
654     {
655         fixedScope = scope;
656     }
657 
658     if(fixedScope.empty())
659     {
660         return "";
661     }
662     const StringList ids = fixIds(splitScopedName(fixedScope));
663 
664     //
665     // Return local scope for "::A::B::C" as A.B.C
666     //
667     stringstream result;
668     for(StringList::const_iterator i = ids.begin(); i != ids.end(); ++i)
669     {
670         if(i != ids.begin())
671         {
672             result << separator;
673         }
674         result << *i;
675     }
676     return result.str();
677 }
678 
679 void
writeMarshalUnmarshalCode(Output & out,const TypePtr & type,const string & param,bool marshal)680 Slice::JsGenerator::writeMarshalUnmarshalCode(Output &out,
681                                               const TypePtr& type,
682                                               const string& param,
683                                               bool marshal)
684 {
685     string stream = marshal ? "ostr" : "istr";
686 
687     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
688     if(builtin)
689     {
690         switch(builtin->kind())
691         {
692             case Builtin::KindByte:
693             {
694                 if(marshal)
695                 {
696                     out << nl << stream << ".writeByte(" << param << ");";
697                 }
698                 else
699                 {
700                     out << nl << param << " = " << stream << ".readByte()" << ';';
701                 }
702                 return;
703             }
704             case Builtin::KindBool:
705             {
706                 if(marshal)
707                 {
708                     out << nl << stream << ".writeBool(" << param << ");";
709                 }
710                 else
711                 {
712                     out << nl << param << " = " << stream << ".readBool()" << ';';
713                 }
714                 return;
715             }
716             case Builtin::KindShort:
717             {
718                 if(marshal)
719                 {
720                     out << nl << stream << ".writeShort(" << param << ");";
721                 }
722                 else
723                 {
724                     out << nl << param << " = " << stream << ".readShort()" << ';';
725                 }
726                 return;
727             }
728             case Builtin::KindInt:
729             {
730                 if(marshal)
731                 {
732                     out << nl << stream << ".writeInt(" << param << ");";
733                 }
734                 else
735                 {
736                     out << nl << param << " = " << stream << ".readInt()" << ';';
737                 }
738                 return;
739             }
740             case Builtin::KindLong:
741             {
742                 if(marshal)
743                 {
744                     out << nl << stream << ".writeLong(" << param << ");";
745                 }
746                 else
747                 {
748                     out << nl << param << " = " << stream << ".readLong()" << ';';
749                 }
750                 return;
751             }
752             case Builtin::KindFloat:
753             {
754                 if(marshal)
755                 {
756                     out << nl << stream << ".writeFloat(" << param << ");";
757                 }
758                 else
759                 {
760                     out << nl << param << " = " << stream << ".readFloat()" << ';';
761                 }
762                 return;
763             }
764             case Builtin::KindDouble:
765             {
766                 if(marshal)
767                 {
768                     out << nl << stream << ".writeDouble(" << param << ");";
769                 }
770                 else
771                 {
772                     out << nl << param << " = " << stream << ".readDouble()" << ';';
773                 }
774                 return;
775             }
776             case Builtin::KindString:
777             {
778                 if(marshal)
779                 {
780                     out << nl << stream << ".writeString(" << param << ");";
781                 }
782                 else
783                 {
784                     out << nl << param << " = " << stream << ".readString()" << ';';
785                 }
786                 return;
787             }
788             case Builtin::KindObject:
789             case Builtin::KindValue:
790             {
791                 // Handle by isClassType below.
792                 break;
793             }
794             case Builtin::KindObjectProxy:
795             {
796                 if(marshal)
797                 {
798                     out << nl << stream << ".writeProxy(" << param << ");";
799                 }
800                 else
801                 {
802                     out << nl << param << " = " << stream << ".readProxy();";
803                 }
804                 return;
805             }
806             case Builtin::KindLocalObject:
807             {
808                 assert(false);
809                 return;
810             }
811         }
812     }
813 
814     if(EnumPtr::dynamicCast(type))
815     {
816         if(marshal)
817         {
818             out << nl << typeToString(type) << "._write(" << stream << ", " << param << ");";
819         }
820         else
821         {
822             out << nl << param << " = " << typeToString(type) << "._read(" << stream << ");";
823         }
824         return;
825     }
826 
827     if(ProxyPtr::dynamicCast(type) || StructPtr::dynamicCast(type))
828     {
829         if(marshal)
830         {
831             out << nl << typeToString(type) << ".write(" << stream << ", " << param << ");";
832         }
833         else
834         {
835             out << nl << param << " = " << typeToString(type) << ".read(" << stream << ", " << param << ");";
836         }
837         return;
838     }
839 
840     if(isClassType(type))
841     {
842         if(marshal)
843         {
844             out << nl << stream << ".writeValue(" << param << ");";
845         }
846         else
847         {
848             out << nl << stream << ".readValue(obj => " << param << " = obj, " << typeToString(type) << ");";
849         }
850         return;
851     }
852 
853     if(SequencePtr::dynamicCast(type) || DictionaryPtr::dynamicCast(type))
854     {
855         if(marshal)
856         {
857             out << nl << getHelper(type) <<".write(" << stream << ", " << param << ");";
858         }
859         else
860         {
861             out << nl << param << " = " << getHelper(type) << ".read(" << stream << ");";
862         }
863         return;
864     }
865 
866     assert(false);
867 }
868 
869 void
writeOptionalMarshalUnmarshalCode(Output & out,const TypePtr & type,const string & param,int tag,bool marshal)870 Slice::JsGenerator::writeOptionalMarshalUnmarshalCode(Output &out,
871                                                       const TypePtr& type,
872                                                       const string& param,
873                                                       int tag,
874                                                       bool marshal)
875 {
876     string stream = marshal ? "ostr" : "istr";
877 
878     if(isClassType(type))
879     {
880         if(marshal)
881         {
882             out << nl << stream << ".writeOptionalValue(" << tag << ", " << param << ");";
883         }
884         else
885         {
886             out << nl << stream << ".readOptionalValue(" << tag << ", obj => " << param << " = obj, "
887                 << typeToString(type) << ");";
888         }
889         return;
890     }
891 
892     if(EnumPtr::dynamicCast(type))
893     {
894         if(marshal)
895         {
896             out << nl << typeToString(type) <<"._writeOpt(" << stream << ", " << tag << ", " << param << ");";
897         }
898         else
899         {
900             out << nl << param << " = " << typeToString(type) << "._readOpt(" << stream << ", " << tag << ");";
901         }
902         return;
903     }
904 
905     if(marshal)
906     {
907         out << nl << getHelper(type) <<".writeOptional(" << stream << ", " << tag << ", " << param << ");";
908     }
909     else
910     {
911         out << nl << param << " = " << getHelper(type) << ".readOptional(" << stream << ", " << tag << ");";
912     }
913 }
914 
915 std::string
getHelper(const TypePtr & type)916 Slice::JsGenerator::getHelper(const TypePtr& type)
917 {
918     BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
919     if(builtin)
920     {
921         switch(builtin->kind())
922         {
923             case Builtin::KindByte:
924             {
925                 return "Ice.ByteHelper";
926             }
927             case Builtin::KindBool:
928             {
929                 return "Ice.BoolHelper";
930             }
931             case Builtin::KindShort:
932             {
933                 return "Ice.ShortHelper";
934             }
935             case Builtin::KindInt:
936             {
937                 return "Ice.IntHelper";
938             }
939             case Builtin::KindLong:
940             {
941                 return "Ice.LongHelper";
942             }
943             case Builtin::KindFloat:
944             {
945                 return "Ice.FloatHelper";
946             }
947             case Builtin::KindDouble:
948             {
949                 return "Ice.DoubleHelper";
950             }
951             case Builtin::KindString:
952             {
953                 return "Ice.StringHelper";
954             }
955             case Builtin::KindObject:
956             case Builtin::KindValue:
957             {
958                 return "Ice.ObjectHelper";
959             }
960             case Builtin::KindObjectProxy:
961             {
962                 return "Ice.ObjectPrx";
963             }
964             case Builtin::KindLocalObject:
965             {
966                 assert(false);
967                 break;
968             }
969         }
970     }
971 
972     if(EnumPtr::dynamicCast(type))
973     {
974         return typeToString(type) + "._helper";
975     }
976 
977     if(StructPtr::dynamicCast(type))
978     {
979         return typeToString(type);
980     }
981 
982     ProxyPtr prx = ProxyPtr::dynamicCast(type);
983     if(prx)
984     {
985         ClassDefPtr def = prx->_class()->definition();
986         if(def->isInterface() || def->allOperations().size() > 0)
987         {
988             return typeToString(type);
989         }
990         else
991         {
992             return "Ice.ObjectPrx";
993         }
994     }
995 
996     if(SequencePtr::dynamicCast(type) || DictionaryPtr::dynamicCast(type))
997     {
998         stringstream s;
999         s << getLocalScope(ContainedPtr::dynamicCast(type)->scoped()) << "Helper";
1000         return s.str();
1001     }
1002 
1003     if(ClassDeclPtr::dynamicCast(type))
1004     {
1005         return "Ice.ObjectHelper";
1006     }
1007 
1008     assert(false);
1009     return "???";
1010 }
1011