1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <IceUtil/CtrlCHandler.h>
6 #include <IceUtil/IceUtil.h>
7 #include <IceUtil/InputUtil.h>
8 #include <IceUtil/Options.h>
9 #include <IceUtil/OutputUtil.h>
10 #include <IceUtil/StringUtil.h>
11 #include <IceUtil/Mutex.h>
12 #include <IceUtil/MutexPtrLock.h>
13 #include <IceUtil/ConsoleUtil.h>
14 #include <Slice/Checksum.h>
15 #include <Slice/Preprocessor.h>
16 #include <Slice/FileTracker.h>
17 #include <Slice/PHPUtil.h>
18 #include <Slice/Parser.h>
19 #include <Slice/Util.h>
20 #include <cstring>
21 #include <climits>
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 
26 #ifdef _WIN32
27 #  include <direct.h>
28 #else
29 #  include <unistd.h>
30 #endif
31 
32 // TODO: fix this warning!
33 #if defined(_MSC_VER)
34 #   pragma warning(disable:4456) // shadow
35 #   pragma warning(disable:4457) // shadow
36 #   pragma warning(disable:4459) // shadow
37 #elif defined(__clang__)
38 #   pragma clang diagnostic ignored "-Wshadow"
39 #elif defined(__GNUC__)
40 #   pragma GCC diagnostic ignored "-Wshadow"
41 #endif
42 
43 using namespace std;
44 using namespace Slice;
45 using namespace Slice::PHP;
46 using namespace IceUtilInternal;
47 
48 namespace
49 {
50 
51 //
52 // Get the fully-qualified name of the given definition. If a suffix is provided,
53 // it is prepended to the definition's unqualified name. If the nameSuffix
54 // is provided, it is appended to the container's name.
55 //
56 string
getAbsolute(const ContainedPtr & cont,bool ns,const string & pfx=std::string (),const string & suffix=std::string ())57 getAbsolute(const ContainedPtr& cont, bool ns, const string& pfx = std::string(), const string& suffix = std::string())
58 {
59     return scopedToName(cont->scope() + pfx + cont->name() + suffix, ns);
60 }
61 
62 }
63 
64 //
65 // CodeVisitor generates the PHP mapping for a translation unit.
66 //
67 class CodeVisitor : public ParserVisitor
68 {
69 public:
70 
71     CodeVisitor(IceUtilInternal::Output&, bool);
72 
73     virtual void visitClassDecl(const ClassDeclPtr&);
74     virtual bool visitClassDefStart(const ClassDefPtr&);
75     virtual bool visitExceptionStart(const ExceptionPtr&);
76     virtual bool visitStructStart(const StructPtr&);
77     virtual void visitSequence(const SequencePtr&);
78     virtual void visitDictionary(const DictionaryPtr&);
79     virtual void visitEnum(const EnumPtr&);
80     virtual void visitConst(const ConstPtr&);
81 
82 private:
83 
84     void startNamespace(const ContainedPtr&);
85     void endNamespace();
86 
87     //
88     // Return the PHP name for the given Slice type. When using namespaces,
89     // this name is a relative (unqualified) name, otherwise this name is the
90     // flattened absolute name.
91     //
92     string getName(const ContainedPtr&, const string& = string());
93 
94     //
95     // Return the PHP variable for the given object's type.
96     //
97     string getTypeVar(const ContainedPtr&, const string& = string());
98 
99     //
100     // Emit the array for a Slice type.
101     //
102     void writeType(const TypePtr&);
103     string getType(const TypePtr&);
104 
105     //
106     // Write a default value for a given type.
107     //
108     void writeDefaultValue(const DataMemberPtr&);
109 
110     struct MemberInfo
111     {
112         string fixedName;
113         bool inherited;
114         DataMemberPtr dataMember;
115     };
116     typedef list<MemberInfo> MemberInfoList;
117 
118     //
119     // Write a member assignment statement for a constructor.
120     //
121     void writeAssign(const MemberInfo&);
122 
123     //
124     // Write constant value.
125     //
126     void writeConstantValue(const TypePtr&, const SyntaxTreeBasePtr&, const string&);
127 
128     //
129     // Write constructor parameters with default values.
130     //
131     void writeConstructorParams(const MemberInfoList&);
132 
133     //
134     // Convert an operation mode into a string.
135     //
136     string getOperationMode(Slice::Operation::Mode, bool);
137 
138     void collectClassMembers(const ClassDefPtr&, MemberInfoList&, bool);
139     void collectExceptionMembers(const ExceptionPtr&, MemberInfoList&, bool);
140 
141     Output& _out;
142     bool _ns; // Using namespaces?
143     list<string> _moduleStack; // TODO: Necessary?
144     set<string> _classHistory; // TODO: Necessary?
145 };
146 
147 //
148 // CodeVisitor implementation.
149 //
CodeVisitor(Output & out,bool ns)150 CodeVisitor::CodeVisitor(Output& out, bool ns) :
151     _out(out),
152     _ns(ns)
153 {
154 }
155 
156 void
visitClassDecl(const ClassDeclPtr & p)157 CodeVisitor::visitClassDecl(const ClassDeclPtr& p)
158 {
159     //
160     // Handle forward declarations.
161     //
162     string scoped = p->scoped();
163     if(_classHistory.count(scoped) == 0)
164     {
165         startNamespace(p);
166 
167         string type = getTypeVar(p);
168         _out << sp << nl << "global " << type << ';';
169 
170         bool isInterface = p->isInterface();
171         ClassDefPtr def = p->definition();
172         if(!p->isLocal() && (isInterface || (def && def->allOperations().size() > 0)))
173         {
174             _out << nl << "global " << type << "Prx;";
175         }
176         _out << nl << "if(!isset(" << type << "))";
177         _out << sb;
178         _out << nl << type << " = IcePHP_declareClass('" << scoped << "');";
179         if(!p->isLocal() && (isInterface || (def && def->allOperations().size() > 0)))
180         {
181             _out << nl << type << "Prx = IcePHP_declareProxy('" << scoped << "');";
182         }
183         _out << eb;
184 
185         endNamespace();
186 
187         _classHistory.insert(scoped); // Avoid redundant declarations.
188     }
189 }
190 
191 bool
visitClassDefStart(const ClassDefPtr & p)192 CodeVisitor::visitClassDefStart(const ClassDefPtr& p)
193 {
194     string scoped = p->scoped();
195     string name = getName(p);
196     string type = getTypeVar(p);
197     string abs = getAbsolute(p, _ns);
198     string prxName = getName(p, "Prx");
199     string prxType = getTypeVar(p, "Prx");
200     string prxAbs = getAbsolute(p, _ns, "", "Prx");
201     ClassList bases = p->bases();
202     ClassDefPtr base;
203     OperationList ops = p->operations();
204     DataMemberList members = p->dataMembers();
205     bool isInterface = p->isInterface();
206     bool isAbstract = isInterface || p->allOperations().size() > 0; // Don't use isAbstract() - see bug 3739
207 
208     startNamespace(p);
209 
210     _out << sp << nl << "global " << type << ';';
211     if(!p->isLocal() && isAbstract)
212     {
213         _out << nl << "global " << prxType << ';';
214     }
215 
216     //
217     // Define the class.
218     //
219     if(isInterface)
220     {
221         if(p->isLocal())
222         {
223             _out << nl << "interface " << name;
224             if(bases.empty())
225             {
226                 if(!p->isLocal())
227                 {
228                     _out << " extends " << scopedToName("::Ice::Object", _ns);
229                 }
230             }
231             else
232             {
233                 _out << " extends ";
234                 for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
235                 {
236                     if(q != bases.begin())
237                     {
238                         _out << ", ";
239                     }
240                     _out << getAbsolute(*q, _ns);
241                 }
242             }
243             _out << sb;
244             for(OperationList::iterator oli = ops.begin(); oli != ops.end(); ++oli)
245             {
246                 _out << nl << "public function " << fixIdent((*oli)->name()) << '(';
247                 ParamDeclList params = (*oli)->parameters();
248                 for(ParamDeclList::iterator q = params.begin(); q != params.end(); ++q)
249                 {
250                     if(q != params.begin())
251                     {
252                         _out << ", ";
253                     }
254                     _out << '$' << fixIdent((*q)->name());
255                 }
256                 _out << ");";
257             }
258 
259             _out << eb;
260         }
261     }
262     else
263     {
264         _out << nl;
265         _out << "class " << name;
266         if(!bases.empty() && !bases.front()->isInterface())
267         {
268             base = bases.front();
269             bases.pop_front();
270         }
271         if(base)
272         {
273             _out << " extends " << getAbsolute(base, _ns);
274         }
275         else
276         {
277             if(!p->isLocal())
278             {
279                 _out << " extends " << scopedToName("::Ice::Value", _ns);
280             }
281         }
282 
283         //
284         // Value objects don't implement any interfaces.
285         //
286         if(p->isLocal() && !bases.empty())
287         {
288             _out << " implements ";
289             for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
290             {
291                 if(q != bases.begin())
292                 {
293                     _out << ", ";
294                 }
295                 _out << getAbsolute(*q, _ns);
296             }
297         }
298 
299         _out << sb;
300 
301         //
302         // __construct
303         //
304         _out << nl << "public function __construct(";
305         MemberInfoList allMembers;
306         collectClassMembers(p, allMembers, false);
307         writeConstructorParams(allMembers);
308         _out << ")";
309         _out << sb;
310         if(base)
311         {
312             _out << nl << "parent::__construct(";
313             int count = 0;
314             for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
315             {
316                 if(q->inherited)
317                 {
318                     if(count)
319                     {
320                         _out << ", ";
321                     }
322                     _out << '$' << q->fixedName;
323                     ++count;
324                 }
325             }
326             _out << ");";
327         }
328         {
329             for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
330             {
331                 if(!q->inherited)
332                 {
333                     writeAssign(*q);
334                 }
335             }
336         }
337         _out << eb;
338 
339         if(!p->isLocal())
340         {
341             //
342             // ice_ice
343             //
344             _out << sp << nl << "public function ice_id()";
345             _out << sb;
346             _out << nl << "return '" << scoped << "';";
347             _out << eb;
348 
349             //
350             // ice_staticId
351             //
352             _out << sp << nl << "public static function ice_staticId()";
353             _out << sb;
354             _out << nl << "return '" << scoped << "';";
355             _out << eb;
356         }
357 
358         //
359         // __toString
360         //
361         _out << sp << nl << "public function __toString()";
362         _out << sb;
363         _out << nl << "global " << type << ';';
364         _out << nl << "return IcePHP_stringify($this, " << type << ");";
365         _out << eb;
366 
367         if(!members.empty())
368         {
369             _out << sp;
370             bool isProtected = p->hasMetaData("protected");
371             for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
372             {
373                 _out << nl;
374                 if(isProtected || (*q)->hasMetaData("protected"))
375                 {
376                     _out << "protected ";
377                 }
378                 else
379                 {
380                     _out << "public ";
381                 }
382                 _out << "$" << fixIdent((*q)->name()) << ";";
383             }
384         }
385 
386         _out << eb; // End of class.
387     }
388 
389     //
390     // Define the proxy class.
391     //
392     if(!p->isLocal() && isAbstract)
393     {
394         _out << sp << nl << "class " << prxName << "Helper";
395         _out << sb;
396 
397         _out << sp << nl << "public static function checkedCast($proxy, $facetOrContext=null, $context=null)";
398         _out << sb;
399         _out << nl << "return $proxy->ice_checkedCast('" << scoped << "', $facetOrContext, $context);";
400         _out << eb;
401 
402         _out << sp << nl << "public static function uncheckedCast($proxy, $facet=null)";
403         _out << sb;
404         _out << nl << "return $proxy->ice_uncheckedCast('" << scoped << "', $facet);";
405         _out << eb;
406 
407         _out << sp << nl << "public static function ice_staticId()";
408         _out << sb;
409         _out << nl << "return '" << scoped << "';";
410         _out << eb;
411 
412         _out << eb;
413     }
414 
415     if(_classHistory.count(scoped) == 0 && p->canBeCyclic())
416     {
417         //
418         // Emit a forward declaration for the class in case a data member refers to this type.
419         //
420         _out << sp << nl << type << " = IcePHP_declareClass('" << scoped << "');";
421         if(!p->isLocal() && isAbstract)
422         {
423             _out << nl << prxType << " = IcePHP_declareProxy('" << scoped << "');";
424         }
425     }
426 
427     {
428         string type;
429         vector<string> seenType;
430         if(base || (!p->isLocal() && !isInterface))
431         {
432             _out << sp << nl << "global ";
433             if(!base)
434             {
435                 type = "$Ice__t_Value";
436             }
437             else
438             {
439                 type = getTypeVar(base);
440             }
441             _out << type << ";";
442         }
443         seenType.push_back(type);
444 
445         for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
446         {
447             string type = getType((*q)->type());
448             if(find(seenType.begin(), seenType.end(), type) == seenType.end())
449             {
450                 seenType.push_back(type);
451                 _out << nl << "global " << type << ";";
452             }
453         }
454     }
455 
456     //
457     // Emit the type information.
458     //
459     const bool preserved = p->hasMetaData("preserve-slice") || p->inheritsMetaData("preserve-slice");
460     _out << nl << type << " = IcePHP_defineClass('" << scoped << "', '" << escapeName(abs) << "', "
461          << p->compactId() << ", " << (preserved ? "true" : "false") << ", "
462          << (isInterface ? "true" : "false") << ", ";
463     if(!base)
464     {
465         if(p->isLocal() || isInterface)
466         {
467             _out << "null";
468         }
469         else
470         {
471             _out << "$Ice__t_Value";
472         }
473     }
474     else
475     {
476         _out << getTypeVar(base);
477     }
478     _out << ", ";
479     //
480     // Members
481     //
482     // Data members are represented as an array:
483     //
484     //   ('MemberName', MemberType, Optional, Tag)
485     //
486     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
487     //
488     if(!members.empty())
489     {
490         _out << "array(";
491         for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
492         {
493             if(q != members.begin())
494             {
495                 _out << ',';
496             }
497             _out.inc();
498             _out << nl << "array('" << fixIdent((*q)->name()) << "', ";
499             writeType((*q)->type());
500             _out << ", " << ((*q)->optional() ? "true" : "false") << ", "
501                  << ((*q)->optional() ? (*q)->tag() : 0) << ')';
502             _out.dec();
503         }
504         _out << ')';
505     }
506     else
507     {
508         _out << "null";
509     }
510     _out << ");";
511 
512     if(!p->isLocal() && isAbstract)
513     {
514         _out << sp << nl << "global ";
515         if(!base || base->allOperations().empty())
516         {
517             _out << "$Ice__t_ObjectPrx";
518         }
519         else
520         {
521             _out << getTypeVar(base, "Prx");
522         }
523         _out << ";";
524         _out << nl << prxType << " = IcePHP_defineProxy('" << scoped << "', ";
525         if(!base || base->allOperations().empty())
526         {
527             _out << "$Ice__t_ObjectPrx";
528         }
529         else
530         {
531             _out << getTypeVar(base, "Prx");
532         }
533         _out << ", ";
534         //
535         // Interfaces
536         //
537         if(!bases.empty())
538         {
539             _out << "array(";
540             for(ClassList::const_iterator q = bases.begin(); q != bases.end(); ++q)
541             {
542                 if(q != bases.begin())
543                 {
544                     _out << ", ";
545                 }
546                 _out << getTypeVar(*q, "Prx");
547             }
548             _out << ')';
549         }
550         else
551         {
552             _out << "null";
553         }
554         _out << ");";
555 
556         //
557         // Define each operation. The arguments to IcePHP_defineOperation are:
558         //
559         // $ClassType, 'opName', Mode, SendMode, FormatType, (InParams), (OutParams), ReturnParam, (Exceptions)
560         //
561         // where InParams and OutParams are arrays of type descriptions, and Exceptions
562         // is an array of exception type ids.
563         //
564         if(!ops.empty())
565         {
566             _out << sp;
567             vector<string> seenTypes;
568             for(OperationList::const_iterator p = ops.begin(); p != ops.end(); ++p)
569             {
570                 ParamDeclList params = (*p)->parameters();
571                 for(ParamDeclList::const_iterator q = params.begin(); q != params.end(); ++q)
572                 {
573                     string type = getType((*q)->type());
574                     if(find(seenTypes.begin(), seenTypes.end(), type) == seenTypes.end())
575                     {
576                         seenTypes.push_back(type);
577                         _out << nl << "global " << type << ";";
578                     }
579                 }
580 
581                 if((*p)->returnType())
582                 {
583                     string type = getType((*p)->returnType());
584                     if(find(seenTypes.begin(), seenTypes.end(), type) == seenTypes.end())
585                     {
586                         seenTypes.push_back(type);
587                         _out << nl << "global " << type << ";";
588                     }
589                 }
590             }
591 
592             for(OperationList::iterator oli = ops.begin(); oli != ops.end(); ++oli)
593             {
594                 ParamDeclList params = (*oli)->parameters();
595                 ParamDeclList::iterator t;
596                 int count;
597 
598                 _out << nl << "IcePHP_defineOperation(" << prxType << ", '" << (*oli)->name() << "', "
599                      << getOperationMode((*oli)->mode(), _ns) << ", " << getOperationMode((*oli)->sendMode(), _ns)
600                      << ", " << static_cast<int>((*oli)->format()) << ", ";
601                 for(t = params.begin(), count = 0; t != params.end(); ++t)
602                 {
603                     if(!(*t)->isOutParam())
604                     {
605                         if(count == 0)
606                         {
607                             _out << "array(";
608                         }
609                         else if(count > 0)
610                         {
611                             _out << ", ";
612                         }
613                         _out << "array(";
614                         writeType((*t)->type());
615                         if((*t)->optional())
616                         {
617                             _out << ", " << (*t)->tag();
618                         }
619                         _out << ')';
620                         ++count;
621                     }
622                 }
623                 if(count > 0)
624                 {
625                     _out << ')';
626                 }
627                 else
628                 {
629                     _out << "null";
630                 }
631                 _out << ", ";
632                 for(t = params.begin(), count = 0; t != params.end(); ++t)
633                 {
634                     if((*t)->isOutParam())
635                     {
636                         if(count == 0)
637                         {
638                             _out << "array(";
639                         }
640                         else if(count > 0)
641                         {
642                             _out << ", ";
643                         }
644                         _out << "array(";
645                         writeType((*t)->type());
646                         if((*t)->optional())
647                         {
648                             _out << ", " << (*t)->tag();
649                         }
650                         _out << ')';
651                         ++count;
652                     }
653                 }
654                 if(count > 0)
655                 {
656                     _out << ')';
657                 }
658                 else
659                 {
660                     _out << "null";
661                 }
662                 _out << ", ";
663                 TypePtr returnType = (*oli)->returnType();
664                 if(returnType)
665                 {
666                     //
667                     // The return type has the same format as an in/out parameter:
668                     //
669                     // Type, Optional?, OptionalTag
670                     //
671                     _out << "array(";
672                     writeType(returnType);
673                     if((*oli)->returnIsOptional())
674                     {
675                         _out << ", " << (*oli)->returnTag();
676                     }
677                     _out << ')';
678                 }
679                 else
680                 {
681                     _out << "null";
682                 }
683                 _out << ", ";
684                 ExceptionList exceptions = (*oli)->throws();
685                 if(!exceptions.empty())
686                 {
687                     _out << "array(";
688                     for(ExceptionList::iterator u = exceptions.begin(); u != exceptions.end(); ++u)
689                     {
690                         if(u != exceptions.begin())
691                         {
692                             _out << ", ";
693                         }
694                         _out << getTypeVar(*u);
695                     }
696                     _out << ')';
697                 }
698                 else
699                 {
700                     _out << "null";
701                 }
702                 _out << ");";
703             }
704         }
705     }
706 
707     endNamespace();
708 
709     if(_classHistory.count(scoped) == 0)
710     {
711         _classHistory.insert(scoped); // Avoid redundant declarations.
712     }
713 
714     return false;
715 }
716 
717 bool
visitExceptionStart(const ExceptionPtr & p)718 CodeVisitor::visitExceptionStart(const ExceptionPtr& p)
719 {
720     string scoped = p->scoped();
721     string name = getName(p);
722     string type = getTypeVar(p);
723     string abs = getAbsolute(p, _ns);
724 
725     startNamespace(p);
726 
727     _out << sp << nl << "global " << type << ';';
728     _out << nl << "class " << name << " extends ";
729     ExceptionPtr base = p->base();
730     string baseName;
731     if(base)
732     {
733         baseName = getAbsolute(base, _ns);
734         _out << baseName;
735     }
736     else if(p->isLocal())
737     {
738         _out << scopedToName("::Ice::LocalException", _ns);
739     }
740     else
741     {
742         _out << scopedToName("::Ice::UserException", _ns);
743     }
744     _out << sb;
745 
746     DataMemberList members = p->dataMembers();
747 
748     //
749     // __construct
750     //
751     _out << nl << "public function __construct(";
752     MemberInfoList allMembers;
753     collectExceptionMembers(p, allMembers, false);
754     writeConstructorParams(allMembers);
755     _out << ")";
756     _out << sb;
757     if(base)
758     {
759         _out << nl << "parent::__construct(";
760         int count = 0;
761         for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
762         {
763             if(q->inherited)
764             {
765                 if(count)
766                 {
767                     _out << ", ";
768                 }
769                 _out << '$' << q->fixedName;
770                 ++count;
771             }
772         }
773         _out << ");";
774     }
775     for(MemberInfoList::iterator q = allMembers.begin(); q != allMembers.end(); ++q)
776     {
777         if(!q->inherited)
778         {
779             writeAssign(*q);
780         }
781     }
782     _out << eb;
783 
784     //
785     // ice_id
786     //
787     _out << sp << nl << "public function ice_id()";
788     _out << sb;
789     _out << nl << "return '" << scoped << "';";
790     _out << eb;
791 
792     //
793     // __toString
794     //
795     _out << sp << nl << "public function __toString()";
796     _out << sb;
797     _out << nl << "global " << type << ';';
798     _out << nl << "return IcePHP_stringifyException($this, " << type << ");";
799     _out << eb;
800 
801     if(!members.empty())
802     {
803         _out << sp;
804         for(DataMemberList::iterator dmli = members.begin(); dmli != members.end(); ++dmli)
805         {
806             _out << nl << "public $" << fixIdent((*dmli)->name()) << ";";
807         }
808     }
809 
810     _out << eb;
811 
812     vector<string> seenType;
813     for(DataMemberList::iterator dmli = members.begin(); dmli != members.end(); ++dmli)
814     {
815         string type = getType((*dmli)->type());
816         if(find(seenType.begin(), seenType.end(), type) == seenType.end())
817         {
818             seenType.push_back(type);
819             _out << nl << "global " << type << ";";
820         }
821     }
822 
823     //
824     // Emit the type information.
825     //
826     const bool preserved = p->hasMetaData("preserve-slice") || p->inheritsMetaData("preserve-slice");
827     _out << sp << nl << type << " = IcePHP_defineException('" << scoped << "', '" << escapeName(abs) << "', "
828          << (preserved ? "true" : "false") << ", ";
829     if(!base)
830     {
831         _out << "null";
832     }
833     else
834     {
835         _out << getTypeVar(base);
836     }
837     _out << ", ";
838     //
839     // Data members are represented as an array:
840     //
841     //   ('MemberName', MemberType, Optional, Tag)
842     //
843     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
844     //
845     if(!members.empty())
846     {
847         _out << "array(";
848         for(DataMemberList::iterator dmli = members.begin(); dmli != members.end(); ++dmli)
849         {
850             if(dmli != members.begin())
851             {
852                 _out << ',';
853             }
854             _out.inc();
855             _out << nl << "array('" << fixIdent((*dmli)->name()) << "', ";
856             writeType((*dmli)->type());
857             _out << ", " << ((*dmli)->optional() ? "true" : "false") << ", "
858                  << ((*dmli)->optional() ? (*dmli)->tag() : 0) << ')';
859             _out.dec();
860         }
861         _out << ')';
862     }
863     else
864     {
865         _out << "null";
866     }
867     _out << ");";
868 
869     endNamespace();
870 
871     return false;
872 }
873 
874 bool
visitStructStart(const StructPtr & p)875 CodeVisitor::visitStructStart(const StructPtr& p)
876 {
877     string scoped = p->scoped();
878     string name = getName(p);
879     string type = getTypeVar(p);
880     string abs = getAbsolute(p, _ns);
881     MemberInfoList memberList;
882 
883     {
884         DataMemberList members = p->dataMembers();
885         for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
886         {
887             memberList.push_back(MemberInfo());
888             memberList.back().fixedName = fixIdent((*q)->name());
889             memberList.back().inherited = false;
890             memberList.back().dataMember = *q;
891         }
892     }
893 
894     startNamespace(p);
895 
896     _out << sp << nl << "global " << type << ';';
897 
898     _out << nl << "class " << name;
899     _out << sb;
900     _out << nl << "public function __construct(";
901     writeConstructorParams(memberList);
902     _out << ")";
903     _out << sb;
904     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
905     {
906         writeAssign(*r);
907     }
908     _out << eb;
909 
910     //
911     // __toString
912     //
913     _out << sp << nl << "public function __toString()";
914     _out << sb;
915     _out << nl << "global " << type << ';';
916     _out << nl << "return IcePHP_stringify($this, " << type << ");";
917     _out << eb;
918 
919     if(!memberList.empty())
920     {
921         _out << sp;
922         for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
923         {
924             _out << nl << "public $" << r->fixedName << ';';
925         }
926     }
927 
928     _out << eb;
929 
930     _out << sp;
931     vector<string> seenType;
932     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
933     {
934         string type = getType(r->dataMember->type());
935         if(find(seenType.begin(), seenType.end(), type) == seenType.end())
936         {
937             seenType.push_back(type);
938             _out << nl << "global " << type << ";";
939         }
940     }
941     //
942     // Emit the type information.
943     //
944     _out << nl << type << " = IcePHP_defineStruct('" << scoped << "', '" << escapeName(abs) << "', array(";
945     //
946     // Data members are represented as an array:
947     //
948     //   ('MemberName', MemberType)
949     //
950     // where MemberType is either a primitive type constant (T_INT, etc.) or the id of a constructed type.
951     //
952     for(MemberInfoList::iterator r = memberList.begin(); r != memberList.end(); ++r)
953     {
954         if(r != memberList.begin())
955         {
956             _out << ",";
957         }
958         _out.inc();
959         _out << nl << "array('" << r->fixedName << "', ";
960         writeType(r->dataMember->type());
961         _out << ')';
962         _out.dec();
963     }
964     _out << "));";
965     endNamespace();
966 
967     return false;
968 }
969 
970 void
visitSequence(const SequencePtr & p)971 CodeVisitor::visitSequence(const SequencePtr& p)
972 {
973     string type = getTypeVar(p);
974     TypePtr content = p->type();
975 
976     startNamespace(p);
977 
978     //
979     // Emit the type information.
980     //
981     string scoped = p->scoped();
982     _out << sp << nl << "global " << type << ';';
983     _out << sp << nl << "if(!isset(" << type << "))";
984     _out << sb;
985     _out << nl << "global " << getType(content) << ";";
986     _out << nl << type << " = IcePHP_defineSequence('" << scoped << "', ";
987     writeType(content);
988     _out << ");";
989     _out << eb;
990 
991     endNamespace();
992 }
993 
994 void
visitDictionary(const DictionaryPtr & p)995 CodeVisitor::visitDictionary(const DictionaryPtr& p)
996 {
997     TypePtr keyType = p->keyType();
998     BuiltinPtr b = BuiltinPtr::dynamicCast(keyType);
999 
1000     const UnitPtr unit = p->unit();
1001     const DefinitionContextPtr dc = unit->findDefinitionContext(p->file());
1002     assert(dc);
1003     if(b)
1004     {
1005         switch(b->kind())
1006         {
1007             case Slice::Builtin::KindBool:
1008             case Slice::Builtin::KindByte:
1009             case Slice::Builtin::KindShort:
1010             case Slice::Builtin::KindInt:
1011             case Slice::Builtin::KindLong:
1012             case Slice::Builtin::KindString:
1013                 //
1014                 // These types are acceptable as dictionary keys.
1015                 //
1016                 break;
1017 
1018             case Slice::Builtin::KindFloat:
1019             case Slice::Builtin::KindDouble:
1020             {
1021                 dc->warning(InvalidMetaData, p->file(), p->line(), "dictionary key type not supported in PHP");
1022                 break;
1023             }
1024 
1025             case Slice::Builtin::KindObject:
1026             case Slice::Builtin::KindObjectProxy:
1027             case Slice::Builtin::KindLocalObject:
1028             case Slice::Builtin::KindValue:
1029                 assert(false);
1030         }
1031     }
1032     else if(!EnumPtr::dynamicCast(keyType))
1033     {
1034         dc->warning(InvalidMetaData, p->file(), p->line(), "dictionary key type not supported in PHP");
1035     }
1036 
1037     string type = getTypeVar(p);
1038 
1039     startNamespace(p);
1040 
1041     //
1042     // Emit the type information.
1043     //
1044     string scoped = p->scoped();
1045     _out << sp << nl << "global " << type << ';';
1046     _out << sp << nl << "if(!isset(" << type << "))";
1047     _out << sb;
1048     _out << nl << "global " << getType(p->keyType()) << ";";
1049     _out << nl << "global " << getType(p->valueType()) << ";";
1050     _out << nl << type << " = IcePHP_defineDictionary('" << scoped << "', ";
1051     writeType(p->keyType());
1052     _out << ", ";
1053     writeType(p->valueType());
1054     _out << ");";
1055     _out << eb;
1056 
1057     endNamespace();
1058 }
1059 
1060 void
visitEnum(const EnumPtr & p)1061 CodeVisitor::visitEnum(const EnumPtr& p)
1062 {
1063     string scoped = p->scoped();
1064     string name = getName(p);
1065     string type = getTypeVar(p);
1066     string abs = getAbsolute(p, _ns);
1067     EnumeratorList enums = p->enumerators();
1068 
1069     startNamespace(p);
1070 
1071     _out << sp << nl << "global " << type << ';';
1072     _out << nl << "class " << name;
1073     _out << sb;
1074 
1075     {
1076         long i = 0;
1077         for(EnumeratorList::iterator q = enums.begin(); q != enums.end(); ++q, ++i)
1078         {
1079             _out << nl << "const " << fixIdent((*q)->name()) << " = " << (*q)->value() << ';';
1080         }
1081     }
1082 
1083     _out << eb;
1084 
1085     //
1086     // Emit the type information.
1087     //
1088     _out << sp << nl << type << " = IcePHP_defineEnum('" << scoped << "', array(";
1089     for(EnumeratorList::iterator q = enums.begin(); q != enums.end(); ++q)
1090     {
1091         if(q != enums.begin())
1092         {
1093             _out << ", ";
1094         }
1095         _out << "'" << (*q)->name() << "', " << (*q)->value();
1096     }
1097     _out << "));";
1098 
1099     endNamespace();
1100 }
1101 
1102 void
visitConst(const ConstPtr & p)1103 CodeVisitor::visitConst(const ConstPtr& p)
1104 {
1105     string name = getName(p);
1106     string type = getTypeVar(p);
1107     string abs = getAbsolute(p, _ns);
1108 
1109     startNamespace(p);
1110 
1111     _out << sp << nl << "if(!defined('" << escapeName(abs) << "'))";
1112     _out << sb;
1113     if(_ns)
1114     {
1115         _out << sp << nl << "define(__NAMESPACE__ . '\\\\" << name << "', ";
1116     }
1117     else
1118     {
1119         _out << sp << nl << "define('" << name << "', ";
1120     }
1121 
1122     writeConstantValue(p->type(), p->valueType(), p->value());
1123 
1124     _out << ");";
1125     _out << eb;
1126 
1127     endNamespace();
1128 }
1129 
1130 void
startNamespace(const ContainedPtr & cont)1131 CodeVisitor::startNamespace(const ContainedPtr& cont)
1132 {
1133     if(_ns)
1134     {
1135         string scope = cont->scope();
1136         scope = scope.substr(2); // Removing leading '::'
1137         scope = scope.substr(0, scope.length() - 2); // Removing trailing '::'
1138         _out << sp << nl << "namespace " << scopedToName(scope, true);
1139         _out << sb;
1140     }
1141 }
1142 
1143 void
endNamespace()1144 CodeVisitor::endNamespace()
1145 {
1146     if(_ns)
1147     {
1148         _out << eb;
1149     }
1150 }
1151 
1152 string
getTypeVar(const ContainedPtr & p,const string & suffix)1153 CodeVisitor::getTypeVar(const ContainedPtr& p, const string& suffix)
1154 {
1155     return "$" + getAbsolute(p, false, "_t_", suffix);
1156 }
1157 
1158 string
getName(const ContainedPtr & p,const string & suffix)1159 CodeVisitor::getName(const ContainedPtr& p, const string& suffix)
1160 {
1161     if(_ns)
1162     {
1163         return fixIdent(p->name() + suffix);
1164     }
1165     else
1166     {
1167         return getAbsolute(p, false, "", suffix);
1168     }
1169 }
1170 
1171 void
writeType(const TypePtr & p)1172 CodeVisitor::writeType(const TypePtr& p)
1173 {
1174     _out << getType(p);
1175 }
1176 
1177 string
getType(const TypePtr & p)1178 CodeVisitor::getType(const TypePtr& p)
1179 {
1180     BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
1181     if(builtin)
1182     {
1183         switch(builtin->kind())
1184         {
1185             case Builtin::KindBool:
1186             {
1187                 return "$IcePHP__t_bool";
1188             }
1189             case Builtin::KindByte:
1190             {
1191                 return "$IcePHP__t_byte";
1192             }
1193             case Builtin::KindShort:
1194             {
1195                 return "$IcePHP__t_short";
1196             }
1197             case Builtin::KindInt:
1198             {
1199                 return "$IcePHP__t_int";
1200             }
1201             case Builtin::KindLong:
1202             {
1203                 return "$IcePHP__t_long";
1204             }
1205             case Builtin::KindFloat:
1206             {
1207                 return "$IcePHP__t_float";
1208             }
1209             case Builtin::KindDouble:
1210             {
1211                 return "$IcePHP__t_double";
1212             }
1213             case Builtin::KindString:
1214             {
1215                 return "$IcePHP__t_string";
1216             }
1217             case Builtin::KindObject:
1218             case Builtin::KindValue:
1219             {
1220                 return "$Ice__t_Value";
1221             }
1222             case Builtin::KindObjectProxy:
1223             {
1224                 return "$Ice__t_ObjectPrx";
1225             }
1226             case Builtin::KindLocalObject:
1227             {
1228                 return "$Ice__t_LocalObject";
1229             }
1230         }
1231     }
1232 
1233     ProxyPtr prx = ProxyPtr::dynamicCast(p);
1234     if(prx)
1235     {
1236         ClassDefPtr def = prx->_class()->definition();
1237         if(def && (def->isInterface() || def->allOperations().size() > 0))
1238         {
1239             return getTypeVar(prx->_class(), "Prx");
1240         }
1241         else
1242         {
1243             return "$Ice__t_ObjectPrx";
1244         }
1245     }
1246 
1247     ContainedPtr cont = ContainedPtr::dynamicCast(p);
1248     assert(cont);
1249     return getTypeVar(cont);
1250 }
1251 
1252 void
writeDefaultValue(const DataMemberPtr & m)1253 CodeVisitor::writeDefaultValue(const DataMemberPtr& m)
1254 {
1255     TypePtr p = m->type();
1256     BuiltinPtr builtin = BuiltinPtr::dynamicCast(p);
1257     if(builtin)
1258     {
1259         switch(builtin->kind())
1260         {
1261             case Builtin::KindBool:
1262             {
1263                 _out << "false";
1264                 break;
1265             }
1266             case Builtin::KindByte:
1267             case Builtin::KindShort:
1268             case Builtin::KindInt:
1269             case Builtin::KindLong:
1270             {
1271                 _out << "0";
1272                 break;
1273             }
1274             case Builtin::KindFloat:
1275             case Builtin::KindDouble:
1276             {
1277                 _out << "0.0";
1278                 break;
1279             }
1280             case Builtin::KindString:
1281             {
1282                 _out << "''";
1283                 break;
1284             }
1285             case Builtin::KindObject:
1286             case Builtin::KindObjectProxy:
1287             case Builtin::KindLocalObject:
1288             case Builtin::KindValue:
1289             {
1290                 _out << "null";
1291                 break;
1292             }
1293         }
1294         return;
1295     }
1296 
1297     EnumPtr en = EnumPtr::dynamicCast(p);
1298     if(en)
1299     {
1300         EnumeratorList enums = en->enumerators();
1301         _out << getAbsolute(en, _ns) << "::" << fixIdent(enums.front()->name());
1302         return;
1303     }
1304 
1305     //
1306     // PHP does not allow the following construct:
1307     //
1308     // function foo($theStruct=new MyStructType)
1309     //
1310     // Instead we use null as the default value and allocate an instance in
1311     // the constructor.
1312     //
1313     if(StructPtr::dynamicCast(p))
1314     {
1315         _out << "null";
1316         return;
1317     }
1318 
1319     _out << "null";
1320 }
1321 
1322 void
writeAssign(const MemberInfo & info)1323 CodeVisitor::writeAssign(const MemberInfo& info)
1324 {
1325     StructPtr st = StructPtr::dynamicCast(info.dataMember->type());
1326     if(st)
1327     {
1328         _out << nl << "$this->" << info.fixedName << " = is_null($" << info.fixedName << ") ? new "
1329              << getAbsolute(st, _ns) << " : $" << info.fixedName << ';';
1330     }
1331     else
1332     {
1333         _out << nl << "$this->" << info.fixedName << " = $" << info.fixedName << ';';
1334     }
1335 }
1336 
1337 void
writeConstantValue(const TypePtr & type,const SyntaxTreeBasePtr & valueType,const string & value)1338 CodeVisitor::writeConstantValue(const TypePtr& type, const SyntaxTreeBasePtr& valueType, const string& value)
1339 {
1340     ConstPtr constant = ConstPtr::dynamicCast(valueType);
1341     if(constant)
1342     {
1343         _out << getAbsolute(constant, _ns);
1344     }
1345     else
1346     {
1347         Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
1348         Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
1349         if(b)
1350         {
1351             switch(b->kind())
1352             {
1353                 case Slice::Builtin::KindBool:
1354                 case Slice::Builtin::KindByte:
1355                 case Slice::Builtin::KindShort:
1356                 case Slice::Builtin::KindInt:
1357                 case Slice::Builtin::KindFloat:
1358                 case Slice::Builtin::KindDouble:
1359                 {
1360                     _out << value;
1361                     break;
1362                 }
1363                 case Slice::Builtin::KindLong:
1364                 {
1365                     IceUtil::Int64 l;
1366                     IceUtilInternal::stringToInt64(value, l);
1367                     //
1368                     // The platform's 'long' type may not be 64 bits, so we store 64-bit
1369                     // values as a string.
1370                     //
1371                     if(sizeof(IceUtil::Int64) > sizeof(long) && (l < LONG_MIN || l > LONG_MAX))
1372                     {
1373                         _out << "'" << value << "'";
1374                     }
1375                     else
1376                     {
1377                         _out << value;
1378                     }
1379                     break;
1380                 }
1381                 case Slice::Builtin::KindString:
1382                 {
1383                     // PHP 7.x also supports an EC6UCN-like notation, see:
1384                     // https://wiki.php.net/rfc/unicode_escape
1385                     //
1386                     _out << "\"" << toStringLiteral(value, "\f\n\r\t\v\x1b", "$", Octal, 0) << "\"";
1387                     break;
1388                 }
1389                 case Slice::Builtin::KindObject:
1390                 case Slice::Builtin::KindObjectProxy:
1391                 case Slice::Builtin::KindLocalObject:
1392                 case Slice::Builtin::KindValue:
1393                     assert(false);
1394             }
1395         }
1396         else if(en)
1397         {
1398             EnumeratorPtr lte = EnumeratorPtr::dynamicCast(valueType);
1399             assert(lte);
1400             _out << getAbsolute(en, _ns) << "::" << fixIdent(lte->name());
1401         }
1402         else
1403         {
1404             assert(false); // Unknown const type.
1405         }
1406     }
1407 }
1408 
1409 void
writeConstructorParams(const MemberInfoList & members)1410 CodeVisitor::writeConstructorParams(const MemberInfoList& members)
1411 {
1412     for(MemberInfoList::const_iterator p = members.begin(); p != members.end(); ++p)
1413     {
1414         if(p != members.begin())
1415         {
1416             _out << ", ";
1417         }
1418         _out << '$' << p->fixedName << "=";
1419 
1420         const DataMemberPtr member = p->dataMember;
1421         if(member->defaultValueType())
1422         {
1423             writeConstantValue(member->type(), member->defaultValueType(), member->defaultValue());
1424         }
1425         else if(member->optional())
1426         {
1427             _out << (_ns ? scopedToName("::Ice::None", _ns) : "Ice_Unset");
1428         }
1429         else
1430         {
1431             writeDefaultValue(member);
1432         }
1433     }
1434 }
1435 
1436 string
getOperationMode(Slice::Operation::Mode mode,bool)1437 CodeVisitor::getOperationMode(Slice::Operation::Mode mode, bool /*ns*/)
1438 {
1439     ostringstream ostr;
1440     ostr << static_cast<int>(mode);
1441     return ostr.str();
1442 }
1443 
1444 void
collectClassMembers(const ClassDefPtr & p,MemberInfoList & allMembers,bool inherited)1445 CodeVisitor::collectClassMembers(const ClassDefPtr& p, MemberInfoList& allMembers, bool inherited)
1446 {
1447     ClassList bases = p->bases();
1448     if(!bases.empty() && !bases.front()->isInterface())
1449     {
1450         collectClassMembers(bases.front(), allMembers, true);
1451     }
1452 
1453     DataMemberList members = p->dataMembers();
1454 
1455     for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
1456     {
1457         MemberInfo m;
1458         m.fixedName = fixIdent((*q)->name());
1459         m.inherited = inherited;
1460         m.dataMember = *q;
1461         allMembers.push_back(m);
1462     }
1463 }
1464 
1465 void
collectExceptionMembers(const ExceptionPtr & p,MemberInfoList & allMembers,bool inherited)1466 CodeVisitor::collectExceptionMembers(const ExceptionPtr& p, MemberInfoList& allMembers, bool inherited)
1467 {
1468     ExceptionPtr base = p->base();
1469     if(base)
1470     {
1471         collectExceptionMembers(base, allMembers, true);
1472     }
1473 
1474     DataMemberList members = p->dataMembers();
1475 
1476     for(DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
1477     {
1478         MemberInfo m;
1479         m.fixedName = fixIdent((*q)->name());
1480         m.inherited = inherited;
1481         m.dataMember = *q;
1482         allMembers.push_back(m);
1483     }
1484 }
1485 
1486 static void
generate(const UnitPtr & un,bool all,bool checksum,bool ns,const vector<string> & includePaths,Output & out)1487 generate(const UnitPtr& un, bool all, bool checksum, bool ns, const vector<string>& includePaths, Output& out)
1488 {
1489     if(!all)
1490     {
1491         vector<string> paths = includePaths;
1492         for(vector<string>::iterator p = paths.begin(); p != paths.end(); ++p)
1493         {
1494             *p = fullPath(*p);
1495         }
1496 
1497         StringList includes = un->includeFiles();
1498         if(!includes.empty())
1499         {
1500             if(ns)
1501             {
1502                 out << sp;
1503                 out << nl << "namespace";
1504                 out << sb;
1505             }
1506             for(StringList::const_iterator q = includes.begin(); q != includes.end(); ++q)
1507             {
1508                 string file = changeInclude(*q, paths);
1509                 out << nl << "require_once '" << file << ".php';";
1510             }
1511             if(ns)
1512             {
1513                 out << eb;
1514             }
1515         }
1516     }
1517 
1518     CodeVisitor codeVisitor(out, ns);
1519     un->visit(&codeVisitor, false);
1520 
1521     if(checksum)
1522     {
1523         ChecksumMap checksums = createChecksums(un);
1524         if(!checksums.empty())
1525         {
1526             out << sp;
1527             if(ns)
1528             {
1529                 out << "namespace"; // Global namespace.
1530                 out << sb;
1531                 out << "new Ice\\SliceChecksumInit(array(";
1532                 for(ChecksumMap::const_iterator p = checksums.begin(); p != checksums.end();)
1533                 {
1534                     out << nl << "\"" << p->first << "\" => \"";
1535                     ostringstream str;
1536                     str.flags(ios_base::hex);
1537                     str.fill('0');
1538                     for(vector<unsigned char>::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
1539                     {
1540                         str << static_cast<int>(*q);
1541                     }
1542                     out << str.str() << "\"";
1543                     if(++p != checksums.end())
1544                     {
1545                         out << ",";
1546                     }
1547                 }
1548                 out << "));";
1549                 out << eb;
1550             }
1551             else
1552             {
1553                 out << nl << "global $Ice_sliceChecksums;";
1554                 for(ChecksumMap::const_iterator p = checksums.begin(); p != checksums.end(); ++p)
1555                 {
1556                     out << nl << "$Ice_sliceChecksums[\"" << p->first << "\"] = \"";
1557                     ostringstream str;
1558                     str.flags(ios_base::hex);
1559                     str.fill('0');
1560                     for(vector<unsigned char>::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
1561                     {
1562                         str << static_cast<int>(*q);
1563                     }
1564                     out << str.str() << "\";";
1565                 }
1566             }
1567         }
1568     }
1569 
1570     out << nl; // Trailing newline.
1571 }
1572 
1573 static void
printHeader(IceUtilInternal::Output & out)1574 printHeader(IceUtilInternal::Output& out)
1575 {
1576     static const char* header =
1577         "//\n"
1578         "// Copyright (c) ZeroC, Inc. All rights reserved.\n"
1579         "//\n"
1580         ;
1581 
1582     out << header;
1583     out << "//\n";
1584     out << "// Ice version " << ICE_STRING_VERSION << "\n";
1585     out << "//\n";
1586 }
1587 
1588 namespace
1589 {
1590 
1591 IceUtil::Mutex* globalMutex = 0;
1592 bool interrupted = false;
1593 
1594 class Init
1595 {
1596 public:
1597 
Init()1598     Init()
1599     {
1600         globalMutex = new IceUtil::Mutex;
1601     }
1602 
~Init()1603     ~Init()
1604     {
1605         delete globalMutex;
1606         globalMutex = 0;
1607     }
1608 };
1609 
1610 Init init;
1611 
1612 }
1613 
1614 static void
interruptedCallback(int)1615 interruptedCallback(int /*signal*/)
1616 {
1617     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
1618 
1619     interrupted = true;
1620 }
1621 
1622 static void
usage(const string & n)1623 usage(const string& n)
1624 {
1625     consoleErr << "Usage: " << n << " [options] slice-files...\n";
1626     consoleErr <<
1627         "Options:\n"
1628         "-h, --help               Show this message.\n"
1629         "-v, --version            Display the Ice version.\n"
1630         "-DNAME                   Define NAME as 1.\n"
1631         "-DNAME=DEF               Define NAME as DEF.\n"
1632         "-UNAME                   Remove any definition for NAME.\n"
1633         "-IDIR                    Put DIR in the include file search path.\n"
1634         "-E                       Print preprocessor output on stdout.\n"
1635         "--output-dir DIR         Create files in the directory DIR.\n"
1636         "-d, --debug              Print debug messages.\n"
1637         "--depend                 Generate Makefile dependencies.\n"
1638         "--depend-xml             Generate dependencies in XML format.\n"
1639         "--depend-file FILE       Write dependencies to FILE instead of standard output.\n"
1640         "--validate               Validate command line options.\n"
1641         "--all                    Generate code for Slice definitions in included files.\n"
1642         "--no-namespace           Do not use PHP namespaces (deprecated).\n"
1643         "--checksum               Generate checksums for Slice definitions.\n"
1644         "--ice                    Allow reserved Ice prefix in Slice identifiers\n"
1645         "                         deprecated: use instead [[\"ice-prefix\"]] metadata.\n"
1646         "--underscore             Allow underscores in Slice identifiers\n"
1647         "                         deprecated: use instead [[\"underscore\"]] metadata.\n"
1648         ;
1649 }
1650 
1651 int
compile(const vector<string> & argv)1652 compile(const vector<string>& argv)
1653 {
1654     IceUtilInternal::Options opts;
1655     opts.addOpt("h", "help");
1656     opts.addOpt("v", "version");
1657     opts.addOpt("", "validate");
1658     opts.addOpt("D", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
1659     opts.addOpt("U", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
1660     opts.addOpt("I", "", IceUtilInternal::Options::NeedArg, "", IceUtilInternal::Options::Repeat);
1661     opts.addOpt("E");
1662     opts.addOpt("", "output-dir", IceUtilInternal::Options::NeedArg);
1663     opts.addOpt("", "depend");
1664     opts.addOpt("", "depend-xml");
1665     opts.addOpt("", "depend-file", IceUtilInternal::Options::NeedArg, "");
1666     opts.addOpt("d", "debug");
1667     opts.addOpt("", "ice");
1668     opts.addOpt("", "underscore");
1669     opts.addOpt("", "all");
1670     opts.addOpt("", "checksum");
1671     opts.addOpt("n", "no-namespace");
1672 
1673     bool validate = find(argv.begin(), argv.end(), "--validate") != argv.end();
1674 
1675     vector<string> args;
1676     try
1677     {
1678         args = opts.parse(argv);
1679     }
1680     catch(const IceUtilInternal::BadOptException& e)
1681     {
1682         consoleErr << argv[0] << ": error: " << e.reason << endl;
1683         if(!validate)
1684         {
1685             usage(argv[0]);
1686         }
1687         return EXIT_FAILURE;
1688     }
1689 
1690     if(opts.isSet("help"))
1691     {
1692         usage(argv[0]);
1693         return EXIT_SUCCESS;
1694     }
1695 
1696     if(opts.isSet("version"))
1697     {
1698         consoleErr << ICE_STRING_VERSION << endl;
1699         return EXIT_SUCCESS;
1700     }
1701 
1702     vector<string> cppArgs;
1703     vector<string> optargs = opts.argVec("D");
1704     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
1705     {
1706         cppArgs.push_back("-D" + *i);
1707     }
1708 
1709     optargs = opts.argVec("U");
1710     for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
1711     {
1712         cppArgs.push_back("-U" + *i);
1713     }
1714 
1715     vector<string> includePaths = opts.argVec("I");
1716     for(vector<string>::const_iterator i = includePaths.begin(); i != includePaths.end(); ++i)
1717     {
1718         cppArgs.push_back("-I" + Preprocessor::normalizeIncludePath(*i));
1719     }
1720 
1721     bool preprocess = opts.isSet("E");
1722 
1723     string output = opts.optArg("output-dir");
1724 
1725     bool depend = opts.isSet("depend");
1726 
1727     bool dependxml = opts.isSet("depend-xml");
1728 
1729     string dependFile = opts.optArg("depend-file");
1730 
1731     bool debug = opts.isSet("debug");
1732 
1733     bool ice = opts.isSet("ice");
1734 
1735     bool underscore = opts.isSet("underscore");
1736 
1737     bool all = opts.isSet("all");
1738 
1739     bool checksum = opts.isSet("checksum");
1740 
1741     bool ns = !opts.isSet("no-namespace");
1742 
1743     if(args.empty())
1744     {
1745         consoleErr << argv[0] << ": error: no input file" << endl;
1746         if(!validate)
1747         {
1748             usage(argv[0]);
1749         }
1750         return EXIT_FAILURE;
1751     }
1752 
1753     if(depend && dependxml)
1754     {
1755         consoleErr << argv[0] << ": error: cannot specify both --depend and --depend-xml" << endl;
1756         if(!validate)
1757         {
1758             usage(argv[0]);
1759         }
1760         return EXIT_FAILURE;
1761     }
1762 
1763     if(validate)
1764     {
1765         return EXIT_SUCCESS;
1766     }
1767 
1768     int status = EXIT_SUCCESS;
1769 
1770     IceUtil::CtrlCHandler ctrlCHandler;
1771     ctrlCHandler.setCallback(interruptedCallback);
1772 
1773     ostringstream os;
1774     if(dependxml)
1775     {
1776         os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<dependencies>" << endl;
1777     }
1778 
1779     for(vector<string>::const_iterator i = args.begin(); i != args.end(); ++i)
1780     {
1781         //
1782         // Ignore duplicates.
1783         //
1784         vector<string>::iterator p = find(args.begin(), args.end(), *i);
1785         if(p != i)
1786         {
1787             continue;
1788         }
1789 
1790         if(depend || dependxml)
1791         {
1792             PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs);
1793             FILE* cppHandle = icecpp->preprocess(false, "-D__SLICE2PHP__");
1794 
1795             if(cppHandle == 0)
1796             {
1797                 return EXIT_FAILURE;
1798             }
1799 
1800             UnitPtr u = Unit::createUnit(false, false, ice, underscore);
1801             int parseStatus = u->parse(*i, cppHandle, debug);
1802             u->destroy();
1803 
1804             if(parseStatus == EXIT_FAILURE)
1805             {
1806                 return EXIT_FAILURE;
1807             }
1808 
1809             if(!icecpp->printMakefileDependencies(os, depend ? Preprocessor::PHP : Preprocessor::SliceXML,
1810                                                   includePaths, "-D__SLICE2PHP__"))
1811             {
1812                 return EXIT_FAILURE;
1813             }
1814 
1815             if(!icecpp->close())
1816             {
1817                 return EXIT_FAILURE;
1818             }
1819         }
1820         else
1821         {
1822             PreprocessorPtr icecpp = Preprocessor::create(argv[0], *i, cppArgs);
1823             FILE* cppHandle = icecpp->preprocess(false, "-D__SLICE2PHP__");
1824 
1825             if(cppHandle == 0)
1826             {
1827                 return EXIT_FAILURE;
1828             }
1829 
1830             if(preprocess)
1831             {
1832                 char buf[4096];
1833                 while(fgets(buf, static_cast<int>(sizeof(buf)), cppHandle) != ICE_NULLPTR)
1834                 {
1835                     if(fputs(buf, stdout) == EOF)
1836                     {
1837                         return EXIT_FAILURE;
1838                     }
1839                 }
1840                 if(!icecpp->close())
1841                 {
1842                     return EXIT_FAILURE;
1843                 }
1844             }
1845             else
1846             {
1847                 UnitPtr u = Unit::createUnit(false, all, ice, underscore);
1848                 int parseStatus = u->parse(*i, cppHandle, debug);
1849 
1850                 if(!icecpp->close())
1851                 {
1852                     u->destroy();
1853                     return EXIT_FAILURE;
1854                 }
1855 
1856                 if(parseStatus == EXIT_FAILURE)
1857                 {
1858                     status = EXIT_FAILURE;
1859                 }
1860                 else
1861                 {
1862                     string base = icecpp->getBaseName();
1863                     string::size_type pos = base.find_last_of("/\\");
1864                     if(pos != string::npos)
1865                     {
1866                         base.erase(0, pos + 1);
1867                     }
1868 
1869                     string file = base + ".php";
1870                     if(!output.empty())
1871                     {
1872                         file = output + '/' + file;
1873                     }
1874 
1875                     try
1876                     {
1877                         IceUtilInternal::Output out;
1878                         out.open(file.c_str());
1879                         if(!out)
1880                         {
1881                             ostringstream os;
1882                             os << "cannot open`" << file << "': " << IceUtilInternal::errorToString(errno);
1883                             throw FileException(__FILE__, __LINE__, os.str());
1884                         }
1885                         FileTracker::instance()->addFile(file);
1886 
1887                         out << "<?php\n";
1888                         printHeader(out);
1889                         printGeneratedHeader(out, base + ".ice");
1890 
1891                         //
1892                         // Generate the PHP mapping.
1893                         //
1894                         generate(u, all, checksum, ns, includePaths, out);
1895 
1896                         out << "?>\n";
1897                         out.close();
1898                     }
1899                     catch(const Slice::FileException& ex)
1900                     {
1901                         // If a file could not be created, then cleanup any
1902                         // created files.
1903                         FileTracker::instance()->cleanup();
1904                         u->destroy();
1905                         consoleErr << argv[0] << ": error: " << ex.reason() << endl;
1906                         return EXIT_FAILURE;
1907                     }
1908                     catch(const string& err)
1909                     {
1910                         FileTracker::instance()->cleanup();
1911                         consoleErr << argv[0] << ": error: " << err << endl;
1912                         status = EXIT_FAILURE;
1913                     }
1914                 }
1915 
1916                 u->destroy();
1917             }
1918         }
1919 
1920         {
1921             IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(globalMutex);
1922 
1923             if(interrupted)
1924             {
1925                 FileTracker::instance()->cleanup();
1926                 return EXIT_FAILURE;
1927             }
1928         }
1929     }
1930 
1931     if(dependxml)
1932     {
1933         os << "</dependencies>\n";
1934     }
1935 
1936     if(depend || dependxml)
1937     {
1938         writeDependencies(os.str(), dependFile);
1939     }
1940 
1941     return status;
1942 }
1943 
1944 #ifdef _WIN32
wmain(int argc,wchar_t * argv[])1945 int wmain(int argc, wchar_t* argv[])
1946 #else
1947 int main(int argc, char* argv[])
1948 #endif
1949 {
1950     vector<string> args = Slice::argvToArgs(argc, argv);
1951     try
1952     {
1953         return compile(args);
1954     }
1955     catch(const std::exception& ex)
1956     {
1957         consoleErr << args[0] << ": error:" << ex.what() << endl;
1958         return EXIT_FAILURE;
1959     }
1960     catch(...)
1961     {
1962         consoleErr << args[0] << ": error:" << "unknown exception" << endl;
1963         return EXIT_FAILURE;
1964     }
1965 }
1966