1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Slice/RubyUtil.h>
6 #include <Slice/Checksum.h>
7 #include <Slice/Util.h>
8 #include <IceUtil/Functional.h>
9 #include <IceUtil/InputUtil.h>
10 #include <iterator>
11 
12 using namespace std;
13 using namespace Slice;
14 using namespace IceUtil;
15 using namespace IceUtilInternal;
16 
17 namespace
18 {
19 
20 string
getEscapedParamName(const OperationPtr & p,const string & name)21 getEscapedParamName(const OperationPtr& p, const string& name)
22 {
23     ParamDeclList params = p->parameters();
24 
25     for(ParamDeclList::const_iterator i = params.begin(); i != params.end(); ++i)
26     {
27         if((*i)->name() == name)
28         {
29             return name + "_";
30         }
31     }
32     return name;
33 }
34 
35 }
36 
37 namespace Slice
38 {
39 namespace Ruby
40 {
41 
42 //
43 // CodeVisitor generates the Ruby mapping for a translation unit.
44 //
45 class CodeVisitor : public ParserVisitor
46 {
47 public:
48 
49     CodeVisitor(IceUtilInternal::Output&);
50 
51     virtual bool visitModuleStart(const ModulePtr&);
52     virtual void visitModuleEnd(const ModulePtr&);
53     virtual void visitClassDecl(const ClassDeclPtr&);
54     virtual bool visitClassDefStart(const ClassDefPtr&);
55     virtual bool visitExceptionStart(const ExceptionPtr&);
56     virtual bool visitStructStart(const StructPtr&);
57     virtual void visitSequence(const SequencePtr&);
58     virtual void visitDictionary(const DictionaryPtr&);
59     virtual void visitEnum(const EnumPtr&);
60     virtual void visitConst(const ConstPtr&);
61 
62 private:
63 
64     //
65     // Return a Ruby symbol for the given parser element.
66     //
67     string getSymbol(const ContainedPtr&);
68 
69     //
70     // Emit Ruby code to assign the given symbol in the current module.
71     //
72     void registerName(const string&);
73 
74     //
75     // Emit the array that describes a Slice type.
76     //
77     void writeType(const TypePtr&);
78 
79     //
80     // Get an initializer value for a given type.
81     //
82     string getInitializer(const DataMemberPtr&);
83 
84     //
85     // Add a value to a hash code.
86     //
87     void writeHash(const string&, const TypePtr&, int&);
88 
89     //
90     // Write a constant value.
91     //
92     void writeConstantValue(const TypePtr&, const SyntaxTreeBasePtr&, const string&);
93 
94     struct MemberInfo
95     {
96         string lowerName; // Mapped name beginning with a lower-case letter for use as the name of a local variable.
97         string fixedName;
98         bool inherited;
99         DataMemberPtr dataMember;
100     };
101     typedef list<MemberInfo> MemberInfoList;
102 
103     //
104     // Write constructor parameters with default values.
105     //
106     void writeConstructorParams(const MemberInfoList&);
107 
108     void collectClassMembers(const ClassDefPtr&, MemberInfoList&, bool);
109     void collectExceptionMembers(const ExceptionPtr&, MemberInfoList&, bool);
110 
111     Output& _out;
112     set<string> _classHistory;
113 };
114 
115 }
116 }
117 
118 static string
lookupKwd(const string & name)119 lookupKwd(const string& name)
120 {
121     //
122     // Keyword list. *Must* be kept in alphabetical order.
123     //
124     // This list only contains keywords that might conflict with a Slice
125     // identifier, so keywords like "defined?" are not necessary.
126     //
127     // This list also contains the names of methods on Object that might
128     // conflict with a Slice identifier, so names such as "inspect" and
129     // "send" are included but "to_s" is not.
130     //
131     static const string keywordList[] =
132     {
133         "BEGIN", "END", "alias", "and", "begin", "break", "case", "class", "clone", "def", "display", "do", "dup",
134         "else", "elsif", "end", "ensure", "extend", "false", "for", "freeze", "hash", "if", "in", "initialize_copy",
135         "inspect", "instance_eval", "instance_variable_get", "instance_variable_set", "instance_variables", "method",
136         "method_missing", "methods", "module", "new", "next", "nil", "not", "object_id", "or", "private_methods",
137         "protected_methods", "public_methods", "redo", "rescue", "retry", "return", "self", "send",
138         "singleton_methods", "super", "taint", "then", "to_a", "to_s", "true", "undef", "unless", "untaint", "until",
139         "when", "while", "yield"
140     };
141     bool found =  binary_search(&keywordList[0],
142                                 &keywordList[sizeof(keywordList) / sizeof(*keywordList)],
143                                 name);
144     return found ? "_" + name : name;
145 }
146 
147 //
148 // Split a scoped name into its components and return the components as a list of (unscoped) identifiers.
149 //
150 static vector<string>
splitScopedName(const string & scoped)151 splitScopedName(const string& scoped)
152 {
153     assert(scoped[0] == ':');
154     vector<string> ids;
155     string::size_type next = 0;
156     string::size_type pos;
157     while((pos = scoped.find("::", next)) != string::npos)
158     {
159         pos += 2;
160         if(pos != scoped.size())
161         {
162             string::size_type endpos = scoped.find("::", pos);
163             if(endpos != string::npos && endpos > pos)
164             {
165                 ids.push_back(scoped.substr(pos, endpos - pos));
166             }
167         }
168         next = pos;
169     }
170     if(next != scoped.size())
171     {
172         ids.push_back(scoped.substr(next));
173     }
174 
175     return ids;
176 }
177 
178 //
179 // CodeVisitor implementation.
180 //
CodeVisitor(Output & out)181 Slice::Ruby::CodeVisitor::CodeVisitor(Output& out) :
182     _out(out)
183 {
184 }
185 
186 bool
visitModuleStart(const ModulePtr & p)187 Slice::Ruby::CodeVisitor::visitModuleStart(const ModulePtr& p)
188 {
189     _out << sp << nl << "module ";
190     //
191     // Ensure that Slice top-level modules are defined as top
192     // level modules in Ruby
193     //
194     if(UnitPtr::dynamicCast(p->container()))
195     {
196         _out << "::";
197     }
198     _out << fixIdent(p->name(), IdentToUpper);
199     _out.inc();
200     return true;
201 }
202 
203 void
visitModuleEnd(const ModulePtr &)204 Slice::Ruby::CodeVisitor::visitModuleEnd(const ModulePtr&)
205 {
206     _out.dec();
207     _out << nl << "end";
208 }
209 
210 void
visitClassDecl(const ClassDeclPtr & p)211 Slice::Ruby::CodeVisitor::visitClassDecl(const ClassDeclPtr& p)
212 {
213     //
214     // Emit forward declarations.
215     //
216     string scoped = p->scoped();
217     if(_classHistory.count(scoped) == 0)
218     {
219         string name = "T_" + fixIdent(p->name(), IdentToUpper);
220         _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper, "T_") << ')';
221         _out.inc();
222         if(p->isLocal())
223         {
224             _out << nl << name << " = ::Ice::__declareLocalClass('" << scoped << "')";
225         }
226         else
227         {
228             _out << nl << name << " = ::Ice::__declareClass('" << scoped << "')";
229             _out << nl << name << "Prx = ::Ice::__declareProxy('" << scoped << "')";
230         }
231         _out.dec();
232         _out << nl << "end";
233         _classHistory.insert(scoped); // Avoid redundant declarations.
234     }
235 }
236 
237 bool
visitClassDefStart(const ClassDefPtr & p)238 Slice::Ruby::CodeVisitor::visitClassDefStart(const ClassDefPtr& p)
239 {
240     bool isInterface = p->isInterface();
241     bool isLocal = p->isLocal();
242     bool isAbstract = isInterface || p->allOperations().size() > 0; // Don't use isAbstract() - see bug 3739
243 
244     //
245     // Do not generate any code for local interfaces.
246     //
247     if(isLocal && isInterface)
248     {
249         return false;
250     }
251 
252     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper) << "_Mixin)";
253     _out.inc();
254 
255     //
256     // Marker to avoid redefinitions, we don't use the actual class names at those might
257     // be defined by IceRuby for some internal classes
258     //
259     _out << sp << nl << "module " << getAbsolute(p, IdentToUpper) << "_Mixin";
260     _out << nl << "end";
261 
262     string scoped = p->scoped();
263     string name = fixIdent(p->name(), IdentToUpper);
264     ClassList bases = p->bases();
265     ClassDefPtr base;
266     OperationList ops = p->operations();
267 
268     DataMemberList members = p->dataMembers();
269 
270     if(isLocal || !isInterface)
271     {
272         if(!bases.empty() && !bases.front()->isInterface())
273         {
274             base = bases.front();
275         }
276 
277         _out << nl << "class " << name;
278         if(base)
279         {
280             _out << " < " << getAbsolute(base, IdentToUpper);
281         }
282         else if(!isLocal)
283         {
284             _out << " < ::Ice::Value";
285         }
286         _out.inc();
287 
288         //
289         // initialize
290         //
291         MemberInfoList allMembers;
292         collectClassMembers(p, allMembers, false);
293         if(!allMembers.empty())
294         {
295             _out << sp << nl << "def initialize(";
296             writeConstructorParams(allMembers);
297             _out << ')';
298             _out.inc();
299 
300             bool inheritsMembers = false;
301             for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
302             {
303                 if(q->inherited)
304                 {
305                     inheritsMembers = true;
306                     break;
307                 }
308             }
309 
310             if(inheritsMembers)
311             {
312                 _out << nl << "super" << spar;
313                 for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
314                 {
315                     if(q->inherited)
316                     {
317                         _out << q->lowerName;
318                     }
319                 }
320                 _out << epar;
321             }
322 
323             for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
324             {
325                 if(!q->inherited)
326                 {
327                     _out << nl << '@' << q->fixedName << " = " << q->lowerName;
328                 }
329             }
330 
331             _out.dec();
332             _out << nl << "end";
333         }
334 
335         //
336         // read/write accessors for data members.
337         //
338         if(!members.empty())
339         {
340             bool prot = p->hasMetaData("protected");
341             DataMemberList protectedMembers;
342 
343             _out << sp << nl << "attr_accessor ";
344             for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
345             {
346                 if(q != members.begin())
347                 {
348                     _out << ", ";
349                 }
350                 _out << ":" << fixIdent((*q)->name(), IdentNormal);
351                 if(prot || (*q)->hasMetaData("protected"))
352                 {
353                     protectedMembers.push_back(*q);
354                 }
355             }
356 
357             if(!protectedMembers.empty())
358             {
359                 _out << nl << "protected ";
360                 for(DataMemberList::iterator q = protectedMembers.begin(); q != protectedMembers.end(); ++q)
361                 {
362                     if(q != protectedMembers.begin())
363                     {
364                         _out << ", ";
365                     }
366                     //
367                     // We need to list the symbols of the reader and the writer (e.g., ":member" and ":member=").
368                     //
369                     _out << ":" << fixIdent((*q)->name(), IdentNormal) << ", :"
370                         << fixIdent((*q)->name(), IdentNormal) << '=';
371                 }
372             }
373         }
374 
375         _out.dec();
376         _out << nl << "end"; // End of class.
377     }
378 
379     //
380     // Generate proxy support. This includes a mix-in module for the proxy's
381     // operations and a class for the proxy itself.
382     //
383     if(!p->isLocal() && isAbstract)
384     {
385         _out << nl << "module " << name << "Prx_mixin";
386         _out.inc();
387         for(ClassList::iterator cli = bases.begin(); cli != bases.end(); ++cli)
388         {
389             ClassDefPtr def = *cli;
390             if(def->isInterface() || def->allOperations().size() > 0)
391             {
392                 _out << nl << "include " << getAbsolute(*cli, IdentToUpper) << "Prx_mixin";
393             }
394         }
395         for(OperationList::iterator oli = ops.begin(); oli != ops.end(); ++oli)
396         {
397             string fixedOpName = fixIdent((*oli)->name(), IdentNormal);
398             if(fixedOpName == "checkedCast" || fixedOpName == "uncheckedCast")
399             {
400                 fixedOpName.insert(0, "_");
401             }
402             TypePtr ret = (*oli)->returnType();
403             ParamDeclList paramList = (*oli)->parameters();
404             string inParams;
405 
406             for(ParamDeclList::const_iterator q = paramList.begin(); q != paramList.end(); ++q)
407             {
408                 if(!(*q)->isOutParam())
409                 {
410                     if(!inParams.empty())
411                     {
412                         inParams.append(", ");
413                     }
414                     inParams.append(fixIdent((*q)->name(), IdentToLower));
415                 }
416             }
417 
418             _out << sp << nl << "def " << fixedOpName << "(";
419             if(!inParams.empty())
420             {
421                 _out << inParams << ", ";
422             }
423             const string contextParamName = getEscapedParamName(*oli, "context");
424             _out << contextParamName << "=nil)";
425             _out.inc();
426             _out << nl << name << "Prx_mixin::OP_" << (*oli)->name() << ".invoke(self, [" << inParams;
427             _out << "], " << contextParamName << ")";
428             _out.dec();
429             _out << nl << "end";
430         }
431         _out.dec();
432         _out << nl << "end"; // End of mix-in module for proxy.
433 
434         _out << sp << nl << "class " << name << "Prx < ::Ice::ObjectPrx";
435         _out.inc();
436         _out << nl << "include ::Ice::Proxy_mixin";
437         _out << nl << "include " << name << "Prx_mixin";
438         _out.dec();
439         _out << nl << "end"; // End of proxy class.
440     }
441 
442     //
443     // Emit type descriptions.
444     //
445     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper, "T_");
446     if(isInterface)
447     {
448         _out << "Prx";
449     }
450     _out << ')';
451     _out.inc();
452     if(p->isLocal())
453     {
454         _out << nl << "T_" << name << " = ::Ice::__declareLocalClass('" << scoped << "')";
455     }
456     else
457     {
458         _out << nl << "T_" << name << " = ::Ice::__declareClass('" << scoped << "')";
459         if(isAbstract)
460         {
461             _out << nl << "T_" << name << "Prx = ::Ice::__declareProxy('" << scoped << "')";
462         }
463     }
464     _out.dec();
465     _out << nl << "end";
466     _classHistory.insert(scoped); // Avoid redundant declarations.
467 
468     const bool preserved = p->hasMetaData("preserve-slice") || p->inheritsMetaData("preserve-slice");
469 
470     _out << sp << nl << "T_" << name << ".defineClass("
471          << (isInterface ? "::Ice::Value" : name) << ", "
472          << p->compactId() << ", "
473          << (preserved ? "true" : "false") << ", "
474          << (isInterface ? "true" : "false") << ", ";
475     if(!base)
476     {
477         _out << "nil";
478     }
479     else
480     {
481         _out << getAbsolute(base, IdentToUpper, "T_");
482     }
483     _out << ", ";
484     //
485     // Members
486     //
487     // Data members are represented as an array:
488     //
489     //   ['MemberName', MemberType, Optional, Tag]
490     //
491     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
492     //
493     _out << "[";
494     if(members.size() > 1)
495     {
496         _out.inc();
497         _out << nl;
498     }
499     {
500         for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
501         {
502             if(q != members.begin())
503             {
504                 _out << ',' << nl;
505             }
506             _out << "['" << fixIdent((*q)->name(), IdentNormal) << "', ";
507             writeType((*q)->type());
508             _out << ", " << ((*q)->optional() ? "true" : "false") << ", " << ((*q)->optional() ? (*q)->tag() : 0)
509                  << ']';
510         }
511     }
512     if(members.size() > 1)
513     {
514         _out.dec();
515         _out << nl;
516     }
517     _out << "])";
518 
519     //
520     // Define each operation. The arguments to __defineOperation are:
521     //
522     // 'opName', Mode, IsAmd, FormatType, [InParams], [OutParams], ReturnParam, [Exceptions]
523     //
524     // where InParams and OutParams are arrays of type descriptions, and Exceptions
525     // is an array of exception types.
526     //
527     if(!p->isLocal() && isAbstract)
528     {
529         _out << sp << nl << "T_" << name << "Prx.defineProxy(" << name << "Prx, ";
530 
531         if(!base || (!base->isInterface() && base->allOperations().size() == 0))
532         {
533             _out << "nil";
534         }
535         else
536         {
537             _out << getAbsolute(base, IdentToUpper, "T_") << "Prx";
538         }
539 
540         //
541         // Interfaces
542         //
543         _out << ", [";
544         {
545             int interfaceCount = 0;
546             for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
547             {
548                 if((*q)->isInterface())
549                 {
550                     if(interfaceCount > 0)
551                     {
552                         _out << ", ";
553                     }
554                     _out << getAbsolute(*q, IdentToUpper, "T_") << "Prx";
555                     ++interfaceCount;
556                 }
557             }
558         }
559         _out << "])";
560 
561         if(!ops.empty())
562         {
563             _out << sp;
564         }
565         for(OperationList::iterator s = ops.begin(); s != ops.end(); ++s)
566         {
567             ParamDeclList params = (*s)->parameters();
568             ParamDeclList::iterator t;
569             int count;
570             string format;
571             switch((*s)->format())
572             {
573             case DefaultFormat:
574                 format = "nil";
575                 break;
576             case CompactFormat:
577                 format = "::Ice::FormatType::CompactFormat";
578                 break;
579             case SlicedFormat:
580                 format = "::Ice::FormatType::SlicedFormat";
581                 break;
582             }
583 
584             _out << nl << name << "Prx_mixin::OP_" << (*s)->name() << " = ::Ice::__defineOperation('"
585                  << (*s)->name() << "', ";
586             switch((*s)->mode())
587             {
588             case Operation::Normal:
589                 _out << "::Ice::OperationMode::Normal";
590                 break;
591             case Operation::Nonmutating:
592                 _out << "::Ice::OperationMode::Nonmutating";
593                 break;
594             case Operation::Idempotent:
595                 _out << "::Ice::OperationMode::Idempotent";
596                 break;
597             }
598             _out << ", ";
599             switch((*s)->sendMode())
600             {
601             case Operation::Normal:
602                 _out << "::Ice::OperationMode::Normal";
603                 break;
604             case Operation::Nonmutating:
605                 _out << "::Ice::OperationMode::Nonmutating";
606                 break;
607             case Operation::Idempotent:
608                 _out << "::Ice::OperationMode::Idempotent";
609                 break;
610             }
611             _out << ", " << ((p->hasMetaData("amd") || (*s)->hasMetaData("amd")) ? "true" : "false") << ", " << format
612                  << ", [";
613             for(t = params.begin(), count = 0; t != params.end(); ++t)
614             {
615                 if(!(*t)->isOutParam())
616                 {
617                     if(count > 0)
618                     {
619                         _out << ", ";
620                     }
621                     _out << '[';
622                     writeType((*t)->type());
623                     _out << ", " << ((*t)->optional() ? "true" : "false") << ", "
624                          << ((*t)->optional() ? (*t)->tag() : 0) << ']';
625                     ++count;
626                 }
627             }
628             _out << "], [";
629             for(t = params.begin(), count = 0; t != params.end(); ++t)
630             {
631                 if((*t)->isOutParam())
632                 {
633                     if(count > 0)
634                     {
635                         _out << ", ";
636                     }
637                     _out << '[';
638                     writeType((*t)->type());
639                     _out << ", " << ((*t)->optional() ? "true" : "false") << ", "
640                          << ((*t)->optional() ? (*t)->tag() : 0) << ']';
641                     ++count;
642                 }
643             }
644             _out << "], ";
645             TypePtr returnType = (*s)->returnType();
646             if(returnType)
647             {
648                 //
649                 // The return type has the same format as an in/out parameter:
650                 //
651                 // Type, Optional?, OptionalTag
652                 //
653                 _out << '[';
654                 writeType(returnType);
655                 _out << ", " << ((*s)->returnIsOptional() ? "true" : "false") << ", "
656                      << ((*s)->returnIsOptional() ? (*s)->returnTag() : 0) << ']';
657             }
658             else
659             {
660                 _out << "nil";
661             }
662             _out << ", [";
663             ExceptionList exceptions = (*s)->throws();
664             for(ExceptionList::iterator u = exceptions.begin(); u != exceptions.end(); ++u)
665             {
666                 if(u != exceptions.begin())
667                 {
668                     _out << ", ";
669                 }
670                 _out << getAbsolute(*u, IdentToUpper, "T_");
671             }
672             _out << "])";
673 
674             string deprecateMetadata;
675             if((*s)->findMetaData("deprecate", deprecateMetadata) || p->findMetaData("deprecate", deprecateMetadata))
676             {
677                 string msg;
678                 string::size_type pos = deprecateMetadata.find(':');
679                 if(pos != string::npos && pos < deprecateMetadata.size() - 1)
680                 {
681                     msg = deprecateMetadata.substr(pos + 1);
682                 }
683                 _out << nl << name << "Prx_mixin::OP_" << (*s)->name() << ".deprecate(\"" << msg << "\")";
684             }
685         }
686     }
687 
688     _out.dec();
689     _out << nl << "end"; // if not defined?()
690 
691     return false;
692 }
693 
694 bool
visitExceptionStart(const ExceptionPtr & p)695 Slice::Ruby::CodeVisitor::visitExceptionStart(const ExceptionPtr& p)
696 {
697     string scoped = p->scoped();
698     string name = fixIdent(p->name(), IdentToUpper);
699 
700     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper) << ')';
701     _out.inc();
702     _out << nl << "class " << name << " < ";
703     ExceptionPtr base = p->base();
704     string baseName;
705     if(base)
706     {
707         baseName = getAbsolute(base, IdentToUpper);
708         _out << baseName;
709     }
710     else if(p->isLocal())
711     {
712         _out << "Ice::LocalException";
713     }
714     else
715     {
716         _out << "Ice::UserException";
717     }
718     _out.inc();
719 
720     DataMemberList members = p->dataMembers();
721 
722     //
723     // initialize
724     //
725     _out << nl << "def initialize";
726     MemberInfoList allMembers;
727     collectExceptionMembers(p, allMembers, false);
728     bool inheritsMembers = false;
729     if(!allMembers.empty())
730     {
731         _out << '(';
732         writeConstructorParams(allMembers);
733         _out << ')';
734         for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
735         {
736             if(q->inherited)
737             {
738                 inheritsMembers = true;
739             }
740         }
741     }
742     _out.inc();
743     if(!allMembers.empty())
744     {
745         if(inheritsMembers)
746         {
747             _out << nl << "super" << spar;
748             for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
749             {
750                 if(q->inherited)
751                 {
752                     _out << q->lowerName;
753                 }
754             }
755             _out << epar;
756         }
757         for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
758         {
759             if(!q->inherited)
760             {
761                 _out << nl << '@' << q->fixedName << " = " << q->lowerName;
762             }
763         }
764     }
765     _out.dec();
766     _out << nl << "end";
767 
768     //
769     // to_s
770     //
771     _out << sp << nl << "def to_s";
772     _out.inc();
773     _out << nl << "'" << scoped << "'";
774     _out.dec();
775     _out << nl << "end";
776 
777     //
778     // read/write accessors for data members.
779     //
780     if(!members.empty())
781     {
782         _out << sp << nl << "attr_accessor ";
783         for(DataMemberList::iterator dmli = members.begin(); dmli != members.end(); ++dmli)
784         {
785             if(dmli != members.begin())
786             {
787                 _out << ", ";
788             }
789             _out << ':' << fixIdent((*dmli)->name(), IdentNormal);
790         }
791     }
792 
793     _out.dec();
794     _out << nl << "end"; // End of class.
795 
796     //
797     // Emit the type information.
798     //
799     const bool preserved = p->hasMetaData("preserve-slice") || p->inheritsMetaData("preserve-slice");
800     _out << sp << nl << "T_" << name << " = ::Ice::__defineException('" << scoped << "', " << name << ", "
801          << (preserved ? "true" : "false") << ", ";
802     if(!base)
803     {
804         _out << "nil";
805     }
806     else
807     {
808          _out << getAbsolute(base, IdentToUpper, "T_");
809     }
810     _out << ", [";
811     if(members.size() > 1)
812     {
813         _out.inc();
814         _out << nl;
815     }
816     //
817     // Data members are represented as an array:
818     //
819     //   ['MemberName', MemberType, Optional, Tag]
820     //
821     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
822     //
823     for(DataMemberList::iterator dmli = members.begin(); dmli != members.end(); ++dmli)
824     {
825         if(dmli != members.begin())
826         {
827             _out << ',' << nl;
828         }
829         _out << "[\"" << fixIdent((*dmli)->name(), IdentNormal) << "\", ";
830         writeType((*dmli)->type());
831         _out << ", " << ((*dmli)->optional() ? "true" : "false") << ", " << ((*dmli)->optional() ? (*dmli)->tag() : 0)
832              << ']';
833     }
834     if(members.size() > 1)
835     {
836         _out.dec();
837         _out << nl;
838     }
839     _out << "])";
840 
841     _out.dec();
842     _out << nl << "end"; // if not defined?()
843 
844     return false;
845 }
846 
847 bool
visitStructStart(const StructPtr & p)848 Slice::Ruby::CodeVisitor::visitStructStart(const StructPtr& p)
849 {
850     string scoped = p->scoped();
851     string name = fixIdent(p->name(), IdentToUpper);
852     MemberInfoList memberList;
853 
854     {
855         DataMemberList members = p->dataMembers();
856         for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
857         {
858             memberList.push_back(MemberInfo());
859             memberList.back().lowerName = fixIdent((*q)->name(), IdentToLower);
860             memberList.back().fixedName = fixIdent((*q)->name(), IdentNormal);
861             memberList.back().inherited = false;
862             memberList.back().dataMember = *q;
863         }
864     }
865 
866     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper) << ')';
867     _out.inc();
868     _out << nl << "class " << name;
869     _out.inc();
870     _out << nl << "include ::Ice::Inspect_mixin";
871     if(!memberList.empty())
872     {
873         _out << nl << "def initialize(";
874         writeConstructorParams(memberList);
875         _out << ")";
876         _out.inc();
877         for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
878         {
879             _out << nl << '@' << r->fixedName << " = " << r->lowerName;
880         }
881         _out.dec();
882         _out << nl << "end";
883     }
884 
885     //
886     // hash
887     //
888     _out << sp << nl << "def hash";
889     _out.inc();
890     _out << nl << "_h = 0";
891     int iter = 0;
892     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
893     {
894         writeHash("@" + r->fixedName, r->dataMember->type(), iter);
895     }
896     _out << nl << "_h % 0x7fffffff";
897     _out.dec();
898     _out << nl << "end";
899 
900     //
901     // ==
902     //
903     _out << sp << nl << "def ==(other)";
904     _out.inc();
905     _out << nl << "return false if";
906     _out.inc();
907     _out << " !other.is_a? " << getAbsolute(p, IdentToUpper);
908     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
909     {
910         _out << " or" << nl << "@" << r->fixedName << " != other." << r->fixedName;
911     }
912     _out.dec();
913     _out << nl << "true";
914     _out.dec();
915     _out << nl << "end";
916 
917     //
918     // eql?
919     //
920     // This method is used to determine the equality of keys in a Hash object.
921     //
922     _out << sp << nl << "def eql?(other)";
923     _out.inc();
924     _out << nl << "return other.class == self.class && other == self";
925     _out.dec();
926     _out << nl << "end";
927 
928     //
929     // read/write accessors for data members.
930     //
931     if(!memberList.empty())
932     {
933         _out << sp << nl << "attr_accessor ";
934         for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
935         {
936             if(r != memberList.begin())
937             {
938                 _out << ", ";
939             }
940             _out << ':' << r->fixedName;
941         }
942     }
943 
944     _out.dec();
945     _out << nl << "end"; // End of class.
946 
947     //
948     // Emit the type information.
949     //
950     _out << sp << nl << "T_" << name << " = ::Ice::__defineStruct('" << scoped << "', " << name << ", [";
951     //
952     // Data members are represented as an array:
953     //
954     //   ['MemberName', MemberType]
955     //
956     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
957     //
958     if(memberList.size() > 1)
959     {
960         _out.inc();
961         _out << nl;
962     }
963     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
964     {
965         if(r != memberList.begin())
966         {
967             _out << ',' << nl;
968         }
969         _out << "[\"" << r->fixedName << "\", ";
970         writeType(r->dataMember->type());
971         _out << ']';
972     }
973     if(memberList.size() > 1)
974     {
975         _out.dec();
976         _out << nl;
977     }
978     _out << "])";
979 
980     _out.dec();
981     _out << nl << "end"; // if not defined?()
982 
983     return false;
984 }
985 
986 void
visitSequence(const SequencePtr & p)987 Slice::Ruby::CodeVisitor::visitSequence(const SequencePtr& p)
988 {
989     //
990     // Emit the type information.
991     //
992     string name = fixIdent(p->name(), IdentToUpper);
993     string scoped = p->scoped();
994     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper, "T_") << ')';
995     _out.inc();
996     _out << nl << "T_" << name << " = ::Ice::__defineSequence('" << scoped << "', ";
997     writeType(p->type());
998     _out << ")";
999     _out.dec();
1000     _out << nl << "end"; // if not defined?()
1001 }
1002 
1003 void
visitDictionary(const DictionaryPtr & p)1004 Slice::Ruby::CodeVisitor::visitDictionary(const DictionaryPtr& p)
1005 {
1006     //
1007     // Emit the type information.
1008     //
1009     string name = fixIdent(p->name(), IdentToUpper);
1010     string scoped = p->scoped();
1011     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper, "T_") << ')';
1012     _out.inc();
1013     _out << nl << "T_" << name << " = ::Ice::__defineDictionary('" << scoped << "', ";
1014     writeType(p->keyType());
1015     _out << ", ";
1016     writeType(p->valueType());
1017     _out << ")";
1018     _out.dec();
1019     _out << nl << "end"; // if not defined?()
1020 }
1021 
1022 void
visitEnum(const EnumPtr & p)1023 Slice::Ruby::CodeVisitor::visitEnum(const EnumPtr& p)
1024 {
1025     string scoped = p->scoped();
1026     string name = fixIdent(p->name(), IdentToUpper);
1027     EnumeratorList enums = p->enumerators();
1028 
1029     _out << sp << nl << "if not defined?(" << getAbsolute(p, IdentToUpper) << ')';
1030     _out.inc();
1031     _out << nl << "class " << name;
1032     _out.inc();
1033     _out << nl << "include Comparable";
1034     _out << sp << nl << "def initialize(name, value)";
1035     _out.inc();
1036     _out << nl << "@name = name";
1037     _out << nl << "@value = value";
1038     _out.dec();
1039     _out << nl << "end";
1040 
1041     //
1042     // from_int
1043     //
1044     {
1045         _out << sp << nl << "def " << name << ".from_int(val)";
1046         ostringstream sz;
1047         sz << enums.size() - 1;
1048         _out.inc();
1049         _out << nl << "@@_enumerators[val]"; // Evaluates to nil if the key is not found
1050         _out.dec();
1051         _out << nl << "end";
1052     }
1053 
1054     //
1055     // to_s
1056     //
1057     _out << sp << nl << "def to_s";
1058     _out.inc();
1059     _out << nl << "@name";
1060     _out.dec();
1061     _out << nl << "end";
1062 
1063     //
1064     // to_i
1065     //
1066     _out << sp << nl << "def to_i";
1067     _out.inc();
1068     _out << nl << "@value";
1069     _out.dec();
1070     _out << nl << "end";
1071 
1072     //
1073     // <=>
1074     //
1075     _out << sp << nl << "def <=>(other)";
1076     _out.inc();
1077     _out << nl << "other.is_a?(" << name << ") or raise ArgumentError, \"value must be a " << name << "\"";
1078     _out << nl << "@value <=> other.to_i";
1079     _out.dec();
1080     _out << nl << "end";
1081 
1082     //
1083     // hash
1084     //
1085     _out << sp << nl << "def hash";
1086     _out.inc();
1087     _out << nl << "@value.hash";
1088     _out.dec();
1089     _out << nl << "end";
1090 
1091     //
1092     // each
1093     //
1094     _out << sp << nl << "def " << name << ".each(&block)";
1095     _out.inc();
1096     _out << nl << "@@_enumerators.each_value(&block)";
1097     _out.dec();
1098     _out << nl << "end";
1099 
1100     //
1101     // Constant for each enumerator.
1102     //
1103     _out << sp;
1104     int i = 0;
1105     for(EnumeratorList::iterator q = enums.begin(); q != enums.end(); ++q, ++i)
1106     {
1107         ostringstream idx;
1108         idx << i;
1109         _out << nl << fixIdent((*q)->name(), IdentToUpper) << " = " << name << ".new(\"" << (*q)->name()
1110              << "\", " << (*q)->value() << ')';
1111     }
1112 
1113     _out << sp << nl << "@@_enumerators = {";
1114     for(EnumeratorList::iterator q = enums.begin(); q != enums.end(); ++q)
1115     {
1116         if(q != enums.begin())
1117         {
1118             _out << ", ";
1119         }
1120         _out << (*q)->value() << "=>" << fixIdent((*q)->name(), IdentToUpper);
1121     }
1122     _out << '}';
1123 
1124     _out << sp << nl << "def " << name << "._enumerators";
1125     _out.inc();
1126     _out << nl << "@@_enumerators";
1127     _out.dec();
1128     _out << nl << "end";
1129 
1130     _out << sp << nl << "private_class_method :new";
1131 
1132     _out.dec();
1133     _out << nl << "end"; // End of class.
1134 
1135     //
1136     // Emit the type information.
1137     //
1138     _out << sp << nl << "T_" << name << " = ::Ice::__defineEnum('" << scoped << "', " << name << ", " << name
1139          << "::_enumerators)";
1140 
1141     _out.dec();
1142     _out << nl << "end"; // if not defined?()
1143 }
1144 
1145 void
visitConst(const ConstPtr & p)1146 Slice::Ruby::CodeVisitor::visitConst(const ConstPtr& p)
1147 {
1148     Slice::TypePtr type = p->type();
1149     string name = fixIdent(p->name(), IdentToUpper);
1150 
1151     _out << sp << nl << name << " = ";
1152     writeConstantValue(type, p->valueType(), p->value());
1153 }
1154 
1155 void
writeType(const TypePtr & p)1156 Slice::Ruby::CodeVisitor::writeType(const TypePtr& p)
1157 {
1158     BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
1159     if(builtin)
1160     {
1161         switch(builtin->kind())
1162         {
1163             case Builtin::KindBool:
1164             {
1165                 _out << "::Ice::T_bool";
1166                 break;
1167             }
1168             case Builtin::KindByte:
1169             {
1170                 _out << "::Ice::T_byte";
1171                 break;
1172             }
1173             case Builtin::KindShort:
1174             {
1175                 _out << "::Ice::T_short";
1176                 break;
1177             }
1178             case Builtin::KindInt:
1179             {
1180                 _out << "::Ice::T_int";
1181                 break;
1182             }
1183             case Builtin::KindLong:
1184             {
1185                 _out << "::Ice::T_long";
1186                 break;
1187             }
1188             case Builtin::KindFloat:
1189             {
1190                 _out << "::Ice::T_float";
1191                 break;
1192             }
1193             case Builtin::KindDouble:
1194             {
1195                 _out << "::Ice::T_double";
1196                 break;
1197             }
1198             case Builtin::KindString:
1199             {
1200                 _out << "::Ice::T_string";
1201                 break;
1202             }
1203             case Builtin::KindValue:
1204             case Builtin::KindObject:
1205             {
1206                 _out << "::Ice::T_Value";
1207                 break;
1208             }
1209             case Builtin::KindObjectProxy:
1210             {
1211                 _out << "::Ice::T_ObjectPrx";
1212                 break;
1213             }
1214             case Builtin::KindLocalObject:
1215             {
1216                 _out << "::Ice::T_LocalObject";
1217                 break;
1218             }
1219         }
1220         return;
1221     }
1222 
1223     ProxyPtr prx = ProxyPtr::dynamicCast(p);
1224     if(prx)
1225     {
1226         ClassDefPtr def = prx->_class()->definition();
1227         if(def->isInterface() || def->allOperations().size() > 0)
1228         {
1229             _out << getAbsolute(prx->_class(), IdentToUpper, "T_") << "Prx";
1230         }
1231         else
1232         {
1233             _out << "::Ice::T_ObjectPrx";
1234         }
1235         return;
1236     }
1237 
1238     ContainedPtr cont = ContainedPtr::dynamicCast(p);
1239     assert(cont);
1240     _out << getAbsolute(cont, IdentToUpper, "T_");
1241 }
1242 
1243 string
getInitializer(const DataMemberPtr & m)1244 Slice::Ruby::CodeVisitor::getInitializer(const DataMemberPtr& m)
1245 {
1246     TypePtr p = m->type();
1247     BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
1248     if(builtin)
1249     {
1250         switch(builtin->kind())
1251         {
1252             case Builtin::KindBool:
1253             {
1254                 return "false";
1255             }
1256             case Builtin::KindByte:
1257             case Builtin::KindShort:
1258             case Builtin::KindInt:
1259             case Builtin::KindLong:
1260             {
1261                 return "0";
1262             }
1263             case Builtin::KindFloat:
1264             case Builtin::KindDouble:
1265             {
1266                 return "0.0";
1267             }
1268             case Builtin::KindString:
1269             {
1270                 return "''";
1271             }
1272             case Builtin::KindValue:
1273             case Builtin::KindObject:
1274             case Builtin::KindObjectProxy:
1275             case Builtin::KindLocalObject:
1276             {
1277                 return "nil";
1278             }
1279         }
1280     }
1281 
1282     EnumPtr en = EnumPtr::dynamicCast(p);
1283     if(en)
1284     {
1285         EnumeratorList enums = en->enumerators();
1286         return getAbsolute(en, IdentToUpper) + "::" + fixIdent(enums.front()->name(), IdentToUpper);
1287     }
1288 
1289     StructPtr st = StructPtr::dynamicCast(p);
1290     if(st)
1291     {
1292         return getAbsolute(st, IdentToUpper) + ".new";
1293     }
1294 
1295     return "nil";
1296 }
1297 
1298 void
writeHash(const string & name,const TypePtr &,int &)1299 Slice::Ruby::CodeVisitor::writeHash(const string& name, const TypePtr&, int&)
1300 {
1301     _out << nl << "_h = 5 * _h + " << name << ".hash";
1302 }
1303 
1304 void
writeConstantValue(const TypePtr & type,const SyntaxTreeBasePtr & valueType,const string & value)1305 Slice::Ruby::CodeVisitor::writeConstantValue(const TypePtr& type, const SyntaxTreeBasePtr& valueType,
1306                                              const string& value)
1307 {
1308     ConstPtr constant = ConstPtr::dynamicCast(valueType);
1309     if(constant)
1310     {
1311         _out << fixIdent(constant->scoped(), IdentToUpper);
1312     }
1313     else
1314     {
1315         Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
1316         Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
1317         if(b)
1318         {
1319             switch(b->kind())
1320             {
1321             case Slice::Builtin::KindBool:
1322             case Slice::Builtin::KindByte:
1323             case Slice::Builtin::KindShort:
1324             case Slice::Builtin::KindInt:
1325             case Slice::Builtin::KindFloat:
1326             case Slice::Builtin::KindDouble:
1327             {
1328                 _out << value;
1329                 break;
1330             }
1331             case Slice::Builtin::KindLong:
1332             {
1333                 IceUtil::Int64 l;
1334                 IceUtilInternal::stringToInt64(value, l);
1335                 _out << value;
1336                 break;
1337             }
1338             case Slice::Builtin::KindString:
1339             {
1340                 // RubyUCN available in Ruby 1.9 or greater
1341                 _out << "\"" << toStringLiteral(value, "\a\b\f\n\r\t\v\x20\x1b", "", EC6UCN, 0) << "\"";
1342                 break;
1343             }
1344 
1345             case Slice::Builtin::KindValue:
1346             case Slice::Builtin::KindObject:
1347             case Slice::Builtin::KindObjectProxy:
1348             case Slice::Builtin::KindLocalObject:
1349                 assert(false);
1350             }
1351         }
1352         else if(en)
1353         {
1354             EnumeratorPtr lte = EnumeratorPtr::dynamicCast(valueType);
1355             assert(lte);
1356             _out << getAbsolute(lte, IdentToUpper);
1357         }
1358         else
1359         {
1360             assert(false); // Unknown const type.
1361         }
1362     }
1363 }
1364 
1365 void
writeConstructorParams(const MemberInfoList & members)1366 Slice::Ruby::CodeVisitor::writeConstructorParams(const MemberInfoList& members)
1367 {
1368     for(MemberInfoList::const_iterator p = members.begin(); p != members.end(); ++p)
1369     {
1370         if(p != members.begin())
1371         {
1372             _out << ", ";
1373         }
1374         _out << p->lowerName << "=";
1375 
1376         const DataMemberPtr member = p->dataMember;
1377         if(member->defaultValueType())
1378         {
1379             writeConstantValue(member->type(), member->defaultValueType(), member->defaultValue());
1380         }
1381         else if(member->optional())
1382         {
1383             _out << "::Ice::Unset";
1384         }
1385         else
1386         {
1387             _out << getInitializer(member);
1388         }
1389     }
1390 }
1391 
1392 void
collectClassMembers(const ClassDefPtr & p,MemberInfoList & allMembers,bool inherited)1393 Slice::Ruby::CodeVisitor::collectClassMembers(const ClassDefPtr& p, MemberInfoList& allMembers, bool inherited)
1394 {
1395     ClassList bases = p->bases();
1396     if(!bases.empty() && !bases.front()->isInterface())
1397     {
1398         collectClassMembers(bases.front(), allMembers, true);
1399     }
1400 
1401     DataMemberList members = p->dataMembers();
1402 
1403     for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
1404     {
1405         MemberInfo m;
1406         m.lowerName = fixIdent((*q)->name(), IdentToLower);
1407         m.fixedName = fixIdent((*q)->name(), IdentNormal);
1408         m.inherited = inherited;
1409         m.dataMember = *q;
1410         allMembers.push_back(m);
1411     }
1412 }
1413 
1414 void
collectExceptionMembers(const ExceptionPtr & p,MemberInfoList & allMembers,bool inherited)1415 Slice::Ruby::CodeVisitor::collectExceptionMembers(const ExceptionPtr& p, MemberInfoList& allMembers, bool inherited)
1416 {
1417     ExceptionPtr base = p->base();
1418     if(base)
1419     {
1420         collectExceptionMembers(base, allMembers, true);
1421     }
1422 
1423     DataMemberList members = p->dataMembers();
1424 
1425     for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
1426     {
1427         MemberInfo m;
1428         m.lowerName = fixIdent((*q)->name(), IdentToLower);
1429         m.fixedName = fixIdent((*q)->name(), IdentNormal);
1430         m.inherited = inherited;
1431         m.dataMember = *q;
1432         allMembers.push_back(m);
1433     }
1434 }
1435 
1436 void
generate(const UnitPtr & un,bool all,bool checksum,const vector<string> & includePaths,Output & out)1437 Slice::Ruby::generate(const UnitPtr& un, bool all, bool checksum, const vector<string>& includePaths, Output& out)
1438 {
1439     out << nl << "require 'Ice'";
1440 
1441     if(!all)
1442     {
1443         vector<string> paths = includePaths;
1444         for(vector<string>::iterator p = paths.begin(); p != paths.end(); ++p)
1445         {
1446             *p = fullPath(*p);
1447         }
1448 
1449         StringList includes = un->includeFiles();
1450         for(StringList::const_iterator q = includes.begin(); q != includes.end(); ++q)
1451         {
1452             string file = changeInclude(*q, paths);
1453             out << nl << "require '" << file << ".rb'";
1454         }
1455     }
1456 
1457     CodeVisitor codeVisitor(out);
1458     un->visit(&codeVisitor, false);
1459 
1460     if(checksum)
1461     {
1462         ChecksumMap checksums = createChecksums(un);
1463         if(!checksums.empty())
1464         {
1465             out << sp;
1466             for(ChecksumMap::const_iterator p = checksums.begin(); p != checksums.end(); ++p)
1467             {
1468                 out << nl << "::Ice::SliceChecksums[\"" << p->first << "\"] = \"";
1469                 ostringstream str;
1470                 str.flags(ios_base::hex);
1471                 str.fill('0');
1472                 for(vector<unsigned char>::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
1473                 {
1474                     str << static_cast<int>(*q);
1475                 }
1476                 out << str.str() << "\"";
1477             }
1478         }
1479     }
1480 
1481     out << nl; // Trailing newline.
1482 }
1483 
1484 string
fixIdent(const string & ident,IdentStyle style)1485 Slice::Ruby::fixIdent(const string& ident, IdentStyle style)
1486 {
1487     assert(!ident.empty());
1488     if(ident[0] != ':')
1489     {
1490         string id = ident;
1491         switch(style)
1492         {
1493         case IdentNormal:
1494             break;
1495         case IdentToUpper:
1496             // Special case BEGIN & END for class/module names.
1497             if(id == "BEGIN" || id == "END")
1498             {
1499                 return id + "_";
1500             }
1501             if(id[0] >= 'a' && id[0] <= 'z')
1502             {
1503                 id[0] += 'A' - 'a';
1504             }
1505             break;
1506         case IdentToLower:
1507             if(id[0] >= 'A' && id[0] <= 'Z')
1508             {
1509                 id[0] += 'a' - 'A';
1510             }
1511             break;
1512         }
1513         return lookupKwd(id);
1514     }
1515 
1516     vector<string> ids = splitScopedName(ident);
1517     assert(!ids.empty());
1518 
1519     ostringstream result;
1520 
1521     for(vector<string>::size_type i = 0; i < ids.size() - 1; ++i)
1522     {
1523         //
1524         // We assume all intermediate names must be upper-case (i.e., they represent
1525         // the names of modules or classes).
1526         //
1527         result << "::" << fixIdent(ids[i], IdentToUpper);
1528     }
1529 
1530     result << "::" << fixIdent(ids[ids.size() - 1], style);
1531 
1532     //
1533     // Preserve trailing scope resolution operator if necessary.
1534     //
1535     if(ident.rfind("::") == ident.size() - 2)
1536     {
1537         result << "::";
1538     }
1539 
1540     return result.str();
1541 }
1542 
1543 string
getAbsolute(const ContainedPtr & cont,IdentStyle style,const string & prefix)1544 Slice::Ruby::getAbsolute(const ContainedPtr& cont, IdentStyle style, const string& prefix)
1545 {
1546     string scope = fixIdent(cont->scope(), IdentToUpper);
1547 
1548     if(prefix.empty())
1549     {
1550         return scope + fixIdent(cont->name(), style);
1551     }
1552     else
1553     {
1554         return scope + prefix + fixIdent(cont->name(), style);
1555     }
1556 }
1557 
1558 void
printHeader(IceUtilInternal::Output & out)1559 Slice::Ruby::printHeader(IceUtilInternal::Output& out)
1560 {
1561     static const char* header =
1562 "#\n"
1563 "# Copyright (c) ZeroC, Inc. All rights reserved.\n"
1564 "#\n"
1565         ;
1566 
1567     out << header;
1568     out << "#\n";
1569     out << "# Ice version " << ICE_STRING_VERSION << "\n";
1570     out << "#\n";
1571 }
1572