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