1 //
2 // class.cc
3 //
4 // Copyright (C) 1996 Limit Point Systems, Inc.
5 //
6 // Author: Curtis Janssen <cljanss@limitpt.com>
7 // Maintainer: LPS
8 //
9 // This file is part of the SC Toolkit.
10 //
11 // The SC Toolkit is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published by
13 // the Free Software Foundation; either version 2, or (at your option)
14 // any later version.
15 //
16 // The SC Toolkit is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with the SC Toolkit; see the file COPYING.LIB.  If not, write to
23 // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 //
25 // The U.S. Government is granted a limited license as per AL 91-7.
26 //
27 
28 #ifdef __GNUG__
29 #pragma implementation
30 #endif
31 
32 #ifdef HAVE_CONFIG_H
33 #include <scconfig.h>
34 #endif
35 
36 #include <string>
37 
38 #include <stdlib.h>
39 #include <string.h>
40 #if defined(HAVE_DLFCN_H)
41 #include <dlfcn.h>
42 #endif // HAVE_DLFCN_H
43 
44 #include <util/misc/formio.h>
45 
46 #include <util/class/class.h>
47 #include <util/class/proxy.h>
48 
49 using namespace std;
50 using namespace sc;
51 
52 std::map<std::string,ClassDescP>* ClassDesc::all_ = 0;
53 std::map<type_info_key,ClassDescP>* ClassDesc::type_info_all_ = 0;
54 char * ClassDesc::classlib_search_path_ = 0;
55 std::set<std::string>* ClassDesc::unresolved_parents_ = 0;
56 
57 /////////////////////////////////////////////////////////////////
58 
59 static sc::ClassDesc DescribedClassProxy_cd(
60     typeid(sc::DescribedClassProxy), "DescribedClassProxy",1,"public DescribedClass");
61 
62 /////////////////////////////////////////////////////////////////
63 
ParentClass(ClassDesc * classdesc,Access access,int is_virtual)64 ParentClass::ParentClass(ClassDesc*classdesc,Access access,int is_virtual):
65   _access(access),
66   _is_virtual(is_virtual),
67   _classdesc(classdesc)
68 {
69 }
70 
ParentClass(const ParentClass & p)71 ParentClass::ParentClass(const ParentClass&p):
72   _access(p._access),
73   _is_virtual(p._is_virtual),
74   _classdesc(p._classdesc)
75 {
76 }
77 
~ParentClass()78 ParentClass::~ParentClass()
79 {
80 }
81 
is_virtual() const82 int ParentClass::is_virtual() const
83 {
84   return _is_virtual;
85 }
86 
classdesc() const87 const ClassDesc* ParentClass::classdesc() const
88 {
89   return _classdesc;
90 }
91 
change_classdesc(ClassDesc * n)92 void ParentClass::change_classdesc(ClassDesc*n)
93 {
94   _classdesc = n;
95 }
96 
97 /////////////////////////////////////////////////////////////////
98 
ParentClasses()99 ParentClasses::ParentClasses():
100   _n(0),
101   _classes(0)
102 {
103 }
104 
105 void
init(const char * parents)106 ParentClasses::init(const char* parents)
107 {
108   // if parents is empty then we are done
109   if (!parents || strlen(parents) == 0) return;
110 
111   char* tokens = ::strcpy(new char[strlen(parents)+1],parents);
112   const char* whitesp = "\t\n,() ";
113   char* token;
114   int is_virtual = 0;
115   ParentClass::Access access = ParentClass::Private;
116   for (token = ::strtok(tokens,whitesp);
117        token;
118        token = ::strtok(0,whitesp)) {
119       if (!strcmp(token,"virtual")) {
120           is_virtual = 1;
121         }
122       else if (!strcmp(token,"public")) {
123           access = ParentClass::Public;
124         }
125       else if (!strcmp(token,"protected")) {
126           access = ParentClass::Protected;
127         }
128       else if (!strcmp(token,"private")) {
129           access = ParentClass::Private;
130         }
131       else {
132           std::string parentkey(token);
133           // if the parents class desc does not exist create a temporary
134           // the temporary will be incorrect,because it does not have the
135           // parent's parents
136           if (ClassDesc::all().find(parentkey) == ClassDesc::all().end()) {
137               ClassDesc *tmp_classdesc = new ClassDesc(token);
138               ClassDesc::all()[parentkey] = tmp_classdesc;
139               if (ClassDesc::unresolved_parents_ == 0) {
140                   ClassDesc::unresolved_parents_ = new std::set<std::string>;
141                 }
142               ClassDesc::unresolved_parents_->insert(token);
143             }
144           ParentClass* p = new ParentClass(ClassDesc::all()[parentkey],
145                                            access,
146                                            is_virtual);
147           add(p);
148           access = ParentClass::Private;
149           is_virtual = 0;
150         }
151     }
152   delete[] tokens;
153 
154 }
155 
~ParentClasses()156 ParentClasses::~ParentClasses()
157 {
158   for (int i=0; i<_n; i++) delete _classes[i];
159 
160   if (_classes) delete[] _classes;
161   _classes = 0;
162   _n = 0;
163 }
164 
165 void
add(ParentClass * p)166 ParentClasses::add(ParentClass*p)
167 {
168   ParentClass** newpp = new ParentClass*[_n+1];
169   for (int i=0; i<_n; i++) newpp[i] = _classes[i];
170   newpp[_n] = p;
171   _n++;
172   delete[] _classes;
173   _classes = newpp;
174 }
175 
176 void
change_parent(ClassDesc * oldcd,ClassDesc * newcd)177 ParentClasses::change_parent(ClassDesc*oldcd,ClassDesc*newcd)
178 {
179   for (int i=0; i<_n; i++) {
180       if (parent(i).classdesc() == oldcd) parent(i).change_classdesc(newcd);
181     }
182 }
183 
184 ////////////////////////////////////////////////////////////////////////
185 
186 type_info_key&
operator =(const type_info_key & t)187 type_info_key::operator=(const type_info_key&t)
188 {
189   ti_ = t.ti_;
190   return *this;
191 }
192 
193 int
operator ==(const type_info_key & t) const194 type_info_key::operator==(const type_info_key&t) const
195 {
196   if (!ti_ && !t.ti_) return 1;
197   if (!ti_ || !t.ti_) return 0;
198 
199   return *ti_ == *t.ti_;
200 }
201 
202 int
operator <(const type_info_key & t) const203 type_info_key::operator<(const type_info_key&t) const
204 {
205   if (!ti_ && !t.ti_) return 0;
206   if (!ti_) return 0;
207   if (!t.ti_) return 1;
208 
209   return ti_->before(*t.ti_);
210 }
211 
212 int
cmp(const type_info_key & t) const213 type_info_key::cmp(const type_info_key&t) const
214 {
215   if (*this == t) return 0;
216   if (*this < t) return -1;
217   return 1;
218 }
219 
220 ////////////////////////////////////////////////////////////////////////
221 
ClassDesc(const type_info & ti,const char * name,int version,const char * parents,DescribedClass * (* ctor)(),DescribedClass * (* keyvalctor)(const Ref<KeyVal> &),DescribedClass * (* stateinctor)(StateIn &))222 ClassDesc::ClassDesc(const type_info &ti,
223                      const char* name, int version,
224                      const char* parents,
225                      DescribedClass* (*ctor)(),
226                      DescribedClass* (*keyvalctor)(const Ref<KeyVal>&),
227                      DescribedClass* (*stateinctor)(StateIn&)
228                      )
229 {
230   if (!type_info_all_) {
231       type_info_all_ = new std::map<type_info_key,ClassDescP>;
232     }
233   type_info_key key(&ti);
234   if (type_info_all_->find(key) != type_info_all_->end()) {
235       ExEnv::err0() << indent
236                    << "ERROR: duplicate ClassDesc detected for class "
237                    << name << " type_info name = " << ti.name() << endl;
238       abort();
239     }
240   else {
241       if (type_info_all_->find(key) == type_info_all_->end()) {
242           (*type_info_all_)[key] = this;
243         }
244       else {
245           // this should never happen
246         }
247     }
248 
249   // test the version number to see if it is valid
250   if (version <= 0) {
251       ExEnv::errn() << "ERROR: ClassDesc ctor: version <= 0" << endl;
252       exit(1);
253     }
254 
255   init(name,version,parents,&ti,ctor,keyvalctor,stateinctor);
256 }
257 
ClassDesc(const char * name)258 ClassDesc::ClassDesc(const char* name)
259 {
260   init(name, 0);
261 }
262 
263 void
init(const char * name,int version,const char * parents,const type_info * ti,DescribedClass * (* ctor)(),DescribedClass * (* keyvalctor)(const Ref<KeyVal> &),DescribedClass * (* stateinctor)(StateIn &))264 ClassDesc::init(const char* name, int version,
265                 const char* parents,
266                 const type_info *ti,
267                 DescribedClass* (*ctor)(),
268                 DescribedClass* (*keyvalctor)(const Ref<KeyVal>&),
269                 DescribedClass* (*stateinctor)(StateIn&))
270 {
271   classname_ = 0;
272   version_ = version;
273   children_ = 0;
274   ctor_ = ctor;
275   keyvalctor_ = keyvalctor;
276   stateinctor_ = stateinctor;
277   ti_ = ti;
278 
279   // make sure that the static members have been initialized
280   if (!all_) {
281       all_ = new std::map<std::string,ClassDescP>;
282       const char* tmp = getenv("LD_LIBRARY_PATH");
283       if (tmp) {
284           // Needed for misbehaving getenv's.
285           if (strncmp(tmp, "LD_LIBRARY_PATH=", 16) == 0) {
286               tmp = ::strchr(tmp,'=');
287               tmp++;
288             }
289         }
290       else tmp = ".";
291       classlib_search_path_ = ::strcpy(new char[strlen(tmp)+1],tmp);
292     }
293 
294   // see if I'm already in the list
295   ClassDesc *me = name_to_class_desc(name);
296   int temp_copy_present = 0;
297   if (me && me->version() != 0) {
298       ExEnv::err0()
299           << indent
300           << "ERROR: ClassDesc ctor: ClassDesc already initialized for "
301           << name << endl;
302       abort();
303     }
304   else if (me) {
305       temp_copy_present = 1;
306     }
307 
308   parents_.init(parents);
309 
310   if (!temp_copy_present && name_to_class_desc(name)) {
311       // I wasn't in the list before, but am in it now
312       ExEnv::err0()
313           << indent
314           << "ERROR: ClassDesc ctor: inheritance loop detected for "
315           << name << endl;
316       abort();
317     }
318 
319   classname_ = ::strcpy(new char[strlen(name)+1],name);
320 
321   std::string key(name);
322 
323   // let each of the parents know that this is a child
324   for (int i=0; i<parents_.n(); i++) {
325       std::string parentkey(parents_[i].classdesc()->name());
326       if (!(*all_)[parentkey]->children_)
327         (*all_)[parentkey]->children_ = new std::set<std::string>;
328       // let the parents know about the child
329       ((*all_)[parentkey]->children_)->insert(key);
330     }
331 
332   // if this class is aleady in all_, then it was put there by a child
333   // preserve children info, destroy the old entry, and put this there
334   if (all_->find(key) != all_->end()) {
335       children_ = (*all_)[key]->children_;
336       (*all_)[key]->children_ = 0;
337 
338       if (!children_) {
339           ExEnv::err0()
340               << indent
341               << "ERROR: ClassDesc: inconsistency in initialization for "
342               << key
343               << "--perhaps a duplicated CTOR call" << endl;
344           abort();
345         }
346 
347       // go thru the list of children and correct their
348       // parent class descriptors
349       for (std::set<std::string>::iterator i=children_->begin();
350            i!=children_->end(); i++) {
351           (*all_)[*i]->change_parent((*all_)[key],this);
352         }
353 
354       delete (*all_)[key];
355       unresolved_parents_->erase(key);
356       if (unresolved_parents_->size() == 0) {
357           delete unresolved_parents_;
358           unresolved_parents_ = 0;
359         }
360     }
361   (*all_)[key] = this;
362 }
363 
~ClassDesc()364 ClassDesc::~ClassDesc()
365 {
366   // remove references to this class descriptor
367   if (children_) {
368       for (std::set<std::string>::iterator i=children_->begin();
369            i!=children_->end(); i++) {
370           if (all_->find(*i) != all_->end()) {
371               (*all_)[*i]->change_parent(this,0);
372             }
373         }
374     }
375   // delete this ClassDesc from the list of all ClassDesc's
376   std::string key(classname_);
377   all_->erase(key);
378 
379   // if the list of all ClassDesc's is empty, delete it
380   if (all_->size() == 0) {
381       delete all_;
382       all_ = 0;
383       delete[] classlib_search_path_;
384       classlib_search_path_ = 0;
385     }
386 
387   // delete this ClassDesc entry from the type_info map
388   if (ti_ != 0) {
389       type_info_key key(ti_);
390       type_info_all_->erase(key);
391       if (type_info_all_->size() == 0) {
392           delete type_info_all_;
393           type_info_all_ = 0;
394         }
395     }
396 
397   // delete local data
398   delete[] classname_;
399   if (children_) delete children_;
400 }
401 
402 ClassDesc*
class_desc(const type_info & ti)403 ClassDesc::class_desc(const type_info &ti)
404 {
405   if (type_info_all_->find(type_info_key(&ti))
406       == type_info_all_->end()) return 0;
407   return (*type_info_all_)[type_info_key(&ti)];
408 }
409 
410 std::map<std::string,ClassDescP>&
all()411 ClassDesc::all()
412 {
413   if (!all_) {
414       ExEnv::errn() << "ClassDesc::all(): all not initialized" << endl;
415       abort();
416     }
417   return *all_;
418 }
419 
420 ClassDesc*
name_to_class_desc(const char * name)421 ClassDesc::name_to_class_desc(const char* name)
422 {
423   std::string key(name);
424   if (all_->find(key) == all_->end()) return 0;
425   return (*all_)[key];
426 }
427 
428 DescribedClass*
create() const429 ClassDesc::create() const
430 {
431   if (ctor_) return (*ctor_)();
432   return 0;
433 }
434 
435 DescribedClass*
create(const Ref<KeyVal> & keyval) const436 ClassDesc::create(const Ref<KeyVal>&keyval) const
437 {
438   DescribedClass* result;
439   if (keyvalctor_) {
440       result = (*keyvalctor_)(keyval);
441     }
442   else result = 0;
443   return result;
444 }
445 
446 DescribedClass*
create(StateIn & statein) const447 ClassDesc::create(StateIn&statein) const
448 {
449   if (stateinctor_) return (*stateinctor_)(statein);
450   return 0;
451 }
452 
453 void
change_parent(ClassDesc * oldcd,ClassDesc * newcd)454 ClassDesc::change_parent(ClassDesc*oldcd,ClassDesc*newcd)
455 {
456   parents_.change_parent(oldcd,newcd);
457 }
458 
459 void
list_all_classes()460 ClassDesc::list_all_classes()
461 {
462   ExEnv::out0() << "Listing all classes:" << endl;
463   for (std::map<std::string,ClassDescP>::iterator ind=all_->begin();
464        ind!=all_->end(); ind++) {
465       ClassDesc* classdesc = ind->second;
466       ExEnv::out0() << "class " << classdesc->name() << endl;
467       ParentClasses& parents = classdesc->parents_;
468       if (parents.n()) {
469           ExEnv::out0() << "  parents:";
470           for (int i=0; i<parents.n(); i++) {
471               if (parents[i].is_virtual()) {
472                   ExEnv::out0() << " virtual";
473                 }
474               if (parents[i].access() == ParentClass::Public) {
475                   ExEnv::out0() << " public";
476                 }
477               else if (parents[i].access() == ParentClass::Protected) {
478                   ExEnv::out0() << " protected";
479                 }
480               if (parents[i].classdesc() == 0) {
481                   ExEnv::errn() << endl
482                                << "ERROR: parent " << i
483                                << " for " << classdesc->name()
484                                << " is missing" << endl;
485                   abort();
486                 }
487               const char *n = parents[i].classdesc()->name();
488               ExEnv::out0() << " " << parents[i].classdesc()->name();
489             }
490           ExEnv::out0() << endl;
491         }
492       std::set<std::string>* children = classdesc->children_;
493       if (children) {
494           ExEnv::out0() << "  children:";
495           for (std::set<std::string>::iterator pind=children->begin();
496                pind!=children->end(); pind++) {
497               ExEnv::out0() << " " << (*pind);
498             }
499           ExEnv::out0() << endl;
500         }
501     }
502 }
503 
create_described_class() const504 DescribedClass* ClassDesc::create_described_class() const
505 {
506     return create();
507 }
508 
509 // Returns 0 for success and -1 for failure.
510 int
load_class(const char * classname)511 ClassDesc::load_class(const char* classname)
512 {
513   // See if the class has already been loaded.
514   if (name_to_class_desc(classname) != 0) {
515       return 0;
516     }
517 
518 #if HAVE_DLFCN_H
519   // make a copy of the library search list
520   char* path = new char[strlen(classlib_search_path_) + 1];
521   strcpy(path, classlib_search_path_);
522 
523   // go through each directory in the library search list
524   char* dir = strtok(path,":");
525   while (dir) {
526       // find the 'classes' files
527       char* filename = new char[strlen(dir) + 8 + 1];
528       strcpy(filename,dir);
529       strcat(filename,"/classes");
530       ExEnv::outn() << "ClassDesc::load_class looking for \"" << filename << "\""
531            << endl;
532       FILE* fp = fopen(filename, "r");
533       delete[] filename;
534 
535       if (fp) {
536           // read the lines in the classes file
537           const int bufsize = 10000;
538           char buf[bufsize];
539           while(fgets(buf, bufsize, fp)) {
540               if (buf[0] != '\0' && buf[strlen(buf)-1] == '\n') {
541                   buf[strlen(buf)-1] = '\0';
542                 }
543               char* lib = strtok(buf," ");
544               char* testclassname = strtok(0," ");
545               ExEnv::outn() << "lib = \"" << lib << "\"" << endl;
546               while(testclassname) {
547                   ExEnv::outn() << "classname = \"" << testclassname << "\"" << endl;
548                   if (strcmp(testclassname,classname) == 0) {
549                       // found it
550                       char* libname = new char[strlen(lib) + strlen(dir) + 2];
551                       strcpy(libname, dir);
552                       strcat(libname, "/");
553                       strcat(libname, lib);
554                       // load the libraries this lib depends upon
555 
556                       // i should look in the library's .dep file to
557                       // get the dependencies, but this makes it a little
558                       // difficult to make sure the same library doesn't
559                       // get loaded twice (which is important) so for now
560                       // i'll just wait until after i load the library and
561                       // then look in the unresolved parents set
562                       // and load parents until nothing is left
563 
564                       // load the library
565                       ExEnv::outn() << "loading \"" << libname << "\"" << endl;
566                       dlopen(libname, RTLD_LAZY);
567 
568                       // load code for parents
569                       while (unresolved_parents_
570                              && unresolved_parents_->size()) {
571                           load_class((*unresolved_parents_->begin()).c_str());
572                         }
573 
574                       fclose(fp);
575                       delete[] path;
576                       // make sure it worked.
577                       if (name_to_class_desc(classname) == 0) {
578                           ExEnv::errn() << "load of \"" << classname << "\" from \""
579                                << libname << "\" failed" << endl;
580                           delete[] libname;
581                           return -1;
582                         }
583                       ExEnv::outn() << "loaded \"" << classname << "\" from \""
584                            << libname << "\"" << endl;
585                       delete[] libname;
586                       return 0;
587                     }
588                   testclassname = strtok(0," ");
589                 }
590             }
591           fclose(fp);
592         }
593 
594       dir = strtok(0, ":");
595     }
596 
597   delete[] path;
598 #endif // HAVE_DLFCN_H
599 
600   ExEnv::outn() << "ClassDesc::load_class(\"" << classname << "\"): load failed"
601        << endl
602        << "Either \"" << classname << "\" is an invalid class name or the code"
603        << endl
604        << "for \"" << classname << "\" was not linked into the executable."
605        << endl;
606 
607   return -1;
608 }
609 
610 ////////////////////////////////////////////////////
611 
612 static ClassDesc DescribedClass_cd(
613     typeid(DescribedClass),"DescribedClass");
614 
DescribedClass()615 DescribedClass::DescribedClass()
616 {
617 }
618 
DescribedClass(const DescribedClass &)619 DescribedClass::DescribedClass(const DescribedClass&) {}
operator =(const DescribedClass &)620 DescribedClass& DescribedClass::operator=(const DescribedClass&)
621 {
622   return *this;
623 }
624 
~DescribedClass()625 DescribedClass::~DescribedClass()
626 {
627 }
628 
629 ClassDesc*
class_desc() const630 DescribedClass::class_desc() const throw()
631 {
632   ClassDesc *cd;
633   try {
634       cd = ClassDesc::class_desc(typeid(*this));
635     }
636   catch (...) {
637       cd = 0;
638     }
639   return cd;
640 }
641 
class_name() const642 const char* DescribedClass::class_name() const
643 {
644     return class_desc()->name();
645 }
646 
class_version() const647 int DescribedClass::class_version() const
648 {
649     return class_desc()->version();
650 }
651 
652 void
print(ostream & o) const653 DescribedClass::print(ostream &o) const
654 {
655   o << indent << "Object of type " << class_name() << endl;
656 }
657 
658 ostream &
operator <<(ostream & o,const RefBase & ref)659 operator <<(ostream&o, const RefBase &ref)
660 {
661   DescribedClass *dc = dynamic_cast<DescribedClass*>(ref.parentpointer());
662   if (dc) {
663       dc->print(o);
664     }
665   else {
666       o << indent << "reference to null" << endl;
667     }
668 
669   return o;
670 }
671 
672 #ifdef EXPLICIT_TEMPLATE_INSTANTIATION
673 
674 template class std::map<std::string,ClassDescP>;
675 
676 template class std::map<std::string,int>;
677 template class std::set<std::string>;
678 
679 #endif
680 
681 /////////////////////////////////////////////////////////////////////////////
682 
683 // Local Variables:
684 // mode: c++
685 // c-file-style: "CLJ"
686 // End:
687